You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android后台Service定时读取BLE设备特征值开发求助

Hey there! Let’s work through the issues with your BlePowerService—specifically keeping it running reliably in the background and fixing those BLE characteristic reading glitches. I’ll break this down into actionable fixes and improvements.

1. Fixing Background Runtime Stability

Android’s background restrictions (especially on API 26+) make it hard for regular Service instances to stay alive. BLE data sync is a legitimate foreground task, so you’ll want to switch to a Foreground Service:

Step 1: Update the Service to be Foreground

Add this code early in your onStartCommand method to create a persistent (low-priority) notification and trigger foreground mode:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // Create a notification channel for Android O+
    NotificationChannel channel = new NotificationChannel(
        "BLE_SYNC_CHANNEL",
        "BLE Data Sync",
        NotificationManager.IMPORTANCE_LOW
    );
    NotificationManager notificationManager = getSystemService(NotificationManager.class);
    notificationManager.createNotificationChannel(channel);

    // Build a minimal foreground notification
    Notification notification = new NotificationCompat.Builder(this, "BLE_SYNC_CHANNEL")
        .setContentTitle("Syncing BLE Device Data")
        .setContentText("Running in background")
        .setSmallIcon(R.drawable.ic_ble_sync) // Replace with your own icon
        .build();

    // Enter foreground mode
    startForeground(1, notification);

    // Rest of your existing onStartCommand logic...
    return START_STICKY; // Ensure service restarts if killed by system
}

Step 2: Add Required Permissions

Add these to your AndroidManifest.xml:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_BLUETOOTH_SCAN" /> <!-- API 31+ -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_BLUETOOTH_CONNECT" /> <!-- API 31+ -->
2. Fixing BLE Characteristic Reading Logic

Your current polling approach with Thread.sleep(1) is inefficient and error-prone. BLE operations are asynchronous—let’s refactor to use callbacks instead:

A. Replace Polling with Callback-Driven Reads

Your GattClientCallback should handle the completion of each read and trigger the next one. Here’s how to adjust it:

private class GattClientCallback extends BluetoothGattCallback {
    @Override
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicRead(gatt, characteristic, status);
        
        if (status == BluetoothGatt.GATT_SUCCESS) {
            // Process the read data (add your existing logic here)
            processCharacteristic(characteristic);
            
            // Trigger next read if queue isn't empty
            if (!ReadQueue.isEmpty()) {
                BluetoothGattCharacteristic nextChar = ReadQueue.remove(0);
                gatt.readCharacteristic(nextChar);
            } else {
                // All reads complete—clean up or start next cycle
                isReading = false;
                disconnectGattIfNeeded();
            }
        } else {
            // Handle read failure (retry or abort)
            Log.e(LOG_CODE, "Failed to read characteristic: " + status);
            isReading = false;
        }
    }

    // Add other required callback methods (onConnectionStateChange, etc.)
}

Then update your TimerTask to kick off the first read instead of polling:

TimerTask timerTask = new TimerTask() {
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    public void run() {
        continuaLetturaForza = true;
        continuaLetturaTemperatura = true;
        int counter = 0;

        while((continuaLetturaForza || continuaLetturaTemperatura) && counter < 25) {
            counter++;
            if (currDevice != null && mGatt != null) {
                if (ReadQueue != null && !ReadQueue.isEmpty() && !isReading) {
                    isReading = true;
                    mGatt.readCharacteristic(ReadQueue.get(0));
                    // Wait for callback to trigger next read—no polling needed!
                    break;
                }
            } else {
                scanLeDevice(true);
                try {
                    Thread.sleep(2500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        if(gattClientCallback!=null && mGatt != null) {
            gattClientCallback.disconnectGattServer();
        }
    }
};

B. Fix Gatt Connection Management

You’re creating new BluetoothGatt instances without cleaning up old ones—this causes memory leaks and connection issues. Add cleanup logic before connecting:

public void connectToDevice(BluetoothDevice device) {
    if (settingApp != null && device.getAddress().equals(settingApp.getAddressBleSX())) {
        // Clean up existing connection first
        if (mGatt != null) {
            mGatt.close();
            mGatt = null;
        }
        currDevice = device;
        gattClientCallback = new GattClientCallback();
        mGatt = currDevice.connectGatt(getBaseContext(), false, gattClientCallback);
        scanLeDevice(false);
    }
}

C. Improve Scan Reliability

  • Your SCAN_PERIOD = 500ms is way too short—most BLE devices take 1-2 seconds to advertise. Increase it to 3000 (3 seconds).
  • Use the modern BluetoothLeScanner API (instead of deprecated LeScanCallback) for better compatibility.
3. Additional Stability Improvements
  • Thread Safety: Mark shared variables like currDevice, mGatt, and isReading with volatile to ensure visibility across threads.
  • Resource Cleanup: Add an onDestroy method to release all resources:
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
        if (mGatt != null) {
            mGatt.close();
            mGatt = null;
        }
        if (db != null) {
            db.close();
        }
        stopForeground(true); // Remove foreground notification
    }
    
  • Database Operations: Move all DB calls to a background thread (e.g., AsyncTask, Coroutines, or Executor) to avoid blocking the main thread and causing ANRs.

内容的提问来源于stack exchange,提问作者bircastri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:30:27