Android Studio+Java开发BLE设备特征属性管理问题求助
BLE特征读/写/通知属性管理问题解决方案
一、修正Properties.java的属性判断逻辑
BLE特征的属性是位掩码组合,不能直接用==判断,必须通过&位运算校验。以下是修复后的完整代码:
import android.bluetooth.BluetoothGattCharacteristic; public class Properties { private final int rawProperties; public Properties(int rawProperties) { this.rawProperties = rawProperties; } // 判断是否支持读操作 public boolean supportsRead() { return (rawProperties & BluetoothGattCharacteristic.PROPERTY_READ) != 0; } // 判断是否支持写操作(包含带响应/无响应两种类型) public boolean supportsWrite() { return (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0 || (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0; } // 区分带响应写 public boolean supportsWriteWithResponse() { return (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE) != 0; } // 区分无响应写 public boolean supportsWriteWithoutResponse() { return (rawProperties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0; } // 判断是否支持通知 public boolean supportsNotification() { return (rawProperties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0; } // 判断是否支持指示 public boolean supportsIndication() { return (rawProperties & BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0; } }
二、优化GattManager.java的实例管理与操作校验
解决Gatt实例泄漏、操作无校验、通知配置不完整等问题,修复后的代码如下:
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.content.Context; import android.util.Log; import java.util.UUID; public class GattManager { private static GattManager instance; private BluetoothGatt mGatt; private Context mContext; private BluetoothDevice mConnectedDevice; private GattManager(Context context) { mContext = context.getApplicationContext(); } // 单例模式,避免重复创建Gatt实例 public static synchronized GattManager getInstance(Context context) { if (instance == null) { instance = new GattManager(context); } return instance; } // 连接设备,自动关闭旧实例防止泄漏 public boolean connect(BluetoothDevice device) { if (mGatt != null) { mGatt.close(); mGatt = null; } mConnectedDevice = device; mGatt = device.connectGatt(mContext, false, mGattCallback); return mGatt != null; } // 断开连接并释放所有资源 public void disconnect() { if (mGatt != null) { mGatt.disconnect(); mGatt.close(); mGatt = null; mConnectedDevice = null; } } // 读取特征:先校验属性,避免无效调用 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mGatt == null || characteristic == null) { return false; } Properties props = new Properties(characteristic.getProperties()); if (!props.supportsRead()) { Log.e("GattManager", "特征不支持读操作"); return false; } return mGatt.readCharacteristic(characteristic); } // 写入特征:根据属性匹配写类型,避免写入失败 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] data) { if (mGatt == null || characteristic == null || data == null) { return false; } Properties props = new Properties(characteristic.getProperties()); if (!props.supportsWrite()) { Log.e("GattManager", "特征不支持写操作"); return false; } characteristic.setValue(data); // 根据属性设置对应的写类型 if (props.supportsWriteWithResponse()) { characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); } else if (props.supportsWriteWithoutResponse()) { characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); } return mGatt.writeCharacteristic(characteristic); } // 设置通知/指示:自动处理CCCD描述符配置 public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enable) { if (mGatt == null || characteristic == null) { return false; } Properties props = new Properties(characteristic.getProperties()); if (!props.supportsNotification() && !props.supportsIndication()) { Log.e("GattManager", "特征不支持通知/指示"); return false; } // 先启用Gatt层通知开关 if (!mGatt.setCharacteristicNotification(characteristic, enable)) { return false; } // 配置CCCD描述符(通知/指示的核心开关) BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (descriptor != null) { byte[] value; if (props.supportsIndication()) { value = enable ? BluetoothGattDescriptor.ENABLE_INDICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE; } else { value = enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE; } descriptor.setValue(value); return mGatt.writeDescriptor(descriptor); } return false; } // 自定义Gatt回调,处理操作结果 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); // 这里可以添加连接状态回调逻辑,比如通知上层界面 } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { // 读取成功,处理特征数据 } else { Log.e("GattCallback", "读特征失败,状态码:" + status); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { // 写入成功 } else { Log.e("GattCallback", "写特征失败,状态码:" + status); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); // 收到通知/指示数据,处理逻辑 } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); // 处理CCCD写入结果 } }; }
三、核心注意事项
- 位掩码判断属性:BLE特征属性是多值组合,必须用
&位运算校验,不能直接用==,否则会忽略多属性共存的情况。 - Gatt实例单例管理:每次连接前关闭旧实例,避免资源泄漏和操作冲突。
- 操作前属性校验:所有读、写、通知操作必须先通过
Properties类校验特征支持性,防止无效调用导致异常。 - 写类型匹配:根据特征的写属性设置对应的
writeType,否则会导致写入失败。 - CCCD描述符配置:启用通知/指示必须写入CCCD描述符,且要区分通知和指示的不同值。
内容的提问来源于stack exchange,提问作者navodsamu
相关产品推荐
相关产品推荐

