Android BLE 特征/描述符读写对应回调未触发问题问询
问题根因
Android 系统BLE栈的GATT操作是严格串行异步的,同一时间只能有一个GATT请求(读特征、写特征、读描述符、写描述符)处于待处理状态。如果在上一个请求的对应回调还没触发时就发起新的GATT请求,新请求会直接返回false,且所有排队的请求都可能卡住、永远不触发对应回调——这是当前遇到无回调、writeCharacteristic返回false的核心原因。
除此之外代码还有两个隐性问题:
- 写入特征前没有显式设置写入类型,系统默认的写入类型和外设实际支持的写入类型不匹配时,会直接导致写入失败
- 没有对获取到的descriptor、service、characteristic对象做有效性校验,若对象获取异常(比如UUID写错、在服务发现完成前缓存了对象),会导致操作实际未发送到底层栈
修复方案
- 实现GATT操作串行队列
所有GATT操作必须按顺序入队,只有当上一个操作的对应回调(onCharacteristicRead/onCharacteristicWrite/onDescriptorRead/onDescriptorWrite)执行完成、返回状态成功后,才从队列中取出下一个操作发起。绝对不要在onServicesDiscovered回调中连续同时发起开通知、写特征、读特征多个请求,这类并发调用会直接堵死GATT操作队列。
标准的开通知+写特征流程应该是:- 服务发现完成后,入队CCC描述符写入请求(使能通知)
- 等待
onDescriptorWrite回调返回status=0(操作成功) - 再入队特征写入/读取请求,发起对应操作
- 补全特征写入配置
在调用characteristic.setValue()之前,显式设置特征的写入类型,两种类型可依次测试匹配外设要求:
不要依赖系统默认的写入类型,第三方BLE工具(比如BLE Scanner)会自动匹配特征支持的写入类型,因此不会出现该问题。// 需要外设返回写入应答,对应PROPERTY_WRITE属性,写入成功后触发onCharacteristicWrite回调 characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); // 不需要外设应答,对应PROPERTY_WRITE_NO_RESPONSE属性,部分自定义BLE外设仅支持该模式 // characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); - 修正对象获取逻辑
- 所有
BluetoothGattService、BluetoothGattCharacteristic、BluetoothGattDescriptor对象必须在onServicesDiscovered回调触发后,通过当前连接的mBluetoothGatt实例实时获取,禁止缓存服务发现完成前拿到的对象,这类对象未绑定当前活跃GATT连接,调用操作会直接失败 - 标准CCC描述符(客户端特征配置描述符)的UUID是固定值
00002902-0000-1000-8000-00805f9b34fb,不要自定义该UUID,获取descriptor后必须加非空判断,避免空对象调用导致操作失效
- 所有
- 统一GATT操作调用线程
所有GATT相关方法(连接、写特征、写描述符、开通知等)建议统一在主线程调用,不要跨多线程发起GATT请求,Android不同系统版本对BLE调用线程的限制不同,跨线程调用很容易出现操作无响应、回调丢失的问题。
快速验证步骤
- 清空所有并发GATT逻辑,在
onServicesDiscovered中仅发起写CCC描述符开通知的操作 - 观察是否收到
onDescriptorWrite回调,若收到且状态为0,再在该回调中发起一次特征写入请求 - 若写入仍返回false,切换写入类型后重试,同时确认特征对象是实时从mBluetoothGatt获取的、未做缓存
内容的提问来源于stack exchange,提问作者Kevin K.
相关产品推荐
相关产品推荐

