Android BLE特征读写代码适配差异及正确性咨询
Hey there! Let's dive into your BLE code and figure out why it's working on some chips but failing on others. There are a few key issues in your current implementation that are likely causing the inconsistency across different BLE hardware.
Key Problems in Your Current Code
1. Incorrect Bitwise Operations for Characteristic Properties
Your checks for characteristic properties (write, read, notify) are using bitwise OR (|) instead of bitwise AND (&), which completely breaks the property validation:
- For example,
(charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0will always evaluate totrue(since OR-ing any value with a non-zero flag will set that bit). This means you're trying to read characteristics even when they don't support the READ property, which will fail on strict BLE chips. - The write property check is overly complex and uses unnecessary OR logic between the two write flags.
2. Concurrent BLE Operations
You're calling writeCharacteristic, readCharacteristic, and setCharacteristicNotification back-to-back without waiting for each operation to complete. BLE operations are asynchronous and must be executed sequentially—most BLE controllers can't handle multiple pending operations at once, which will cause failures on chips that enforce this rule.
3. Incomplete Notification Setup (Possible)
While your code calls setCharacteristicNotification, some BLE services require writing to the Client Characteristic Configuration Descriptor (CCCD) explicitly to enable notifications from the device. If your BluetoothLeService doesn't handle this internally, notifications won't work on chips that require this step.
Corrected Implementation
Here's a revised version of your code that fixes these issues, plus notes on handling asynchronous operations properly:
Step 1: Fix Property Checks
Use bitwise AND to correctly validate if a characteristic supports a specific property:
if (BluetoothLeService.mBluetoothGatt.getService(BluetoothLeService.UUID_SERVICE) != null) { BluetoothGattService gattService = BluetoothLeService.mBluetoothGatt.getService(BluetoothLeService.UUID_SERVICE); BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(BluetoothLeService.UUID_CHARACTERISTICS); if (characteristic != null) { boolean uuidOK = true; final int charaProp = characteristic.getProperties(); // Check if write (with or without response) is supported if ((charaProp & (BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) != 0) { // Start write operation first (wait for callback before next step) BluetoothLeService.writeCharacteristic(new byte[]{0, 10, 10}); } // Check if read is supported if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) != 0) { // Clear existing notifications only if needed if (mNotifyCharacteristic != null && mNotifyCharacteristic != characteristic) { mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, false); mNotifyCharacteristic = null; } // Wait for write callback before triggering read! // We'll move this to the onCharacteristicWrite callback } // Check if notify is supported if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) { mNotifyCharacteristic = characteristic; // Enable notification (ensure your service handles CCCD write internally) boolean notificationEnabled = mBluetoothLeService.setCharacteristicNotification(characteristic, true); if (notificationEnabled) { // Explicitly write CCCD if your service doesn't do this automatically BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (descriptor != null) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothLeService.writeDescriptor(descriptor); } } } } }
Step 2: Handle Asynchronous Operations Sequentially
Modify your BluetoothLeService to trigger the next operation only after the previous one completes via callbacks:
For example, in your onCharacteristicWrite callback:
@Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { // Write succeeded, now trigger read if needed if (mNotifyCharacteristic != null && (mNotifyCharacteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0) { readCharacteristic(mNotifyCharacteristic); } } else { // Handle write failure (e.g., log an error or retry) } }
Step 3: Verify CCCD Handling
Ensure your setCharacteristicNotification method in BluetoothLeService handles writing the CCCD descriptor. A typical implementation looks like this:
public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothGatt == null || characteristic == null) { return false; } boolean success = mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); if (success) { BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (descriptor != null) { descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); success = mBluetoothGatt.writeDescriptor(descriptor); } } return success; }
Final Notes
- Always check the status code in BLE callbacks (
onCharacteristicWrite,onCharacteristicRead,onDescriptorWrite) to handle failures gracefully. - Avoid holding static references to
mBluetoothGatt(likeBluetoothLeService.mBluetoothGatt)—this can cause memory leaks and unexpected behavior if multiple connections are attempted. - Test with a range of BLE chips to ensure compatibility, as different vendors may have slightly different implementations of the BLE spec.
内容的提问来源于stack exchange,提问作者RareBoy

