BLE多特征值读取异常:Kotlin Compose应用双特征通知故障
BLE特征读取异常:交替读取正常、重启后仅单特征可读的解决方案
问题根源
你的代码在onServicesDiscovered中连续调用了两次gatt.readCharacteristic(),但Android BLE的操作是串行异步的——同一时间只能处理一个BLE请求,连续发起的第二个读请求会被系统忽略或排队失败。这就是为什么只有第一个特征能稳定读取,交换UUID后偶然正常是因为设备响应顺序的巧合,重启后又回到初始状态。而nRF工具内部做了串行请求的处理,所以能正常读取两个特征。
解决方案
必须等第一个特征的读取回调onCharacteristicRead完成后,再发起第二个特征的读取请求,严格遵循BLE操作的串行规则。
修改后的代码示例
- 调整
onServicesDiscovered,仅发起第一个特征读取
@SuppressLint("MissingPermission") override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) { when (status) { BluetoothGatt.GATT_SUCCESS -> { val service = gatt?.getService(SERVICE_UUID) service?.let { // 先读取volts特征 val voltsCharacteristic = it.getCharacteristic(VOLTS_UUID) voltsCharacteristic?.let { char -> gatt.readCharacteristic(char) Log.d("Frank - read voltsCharacteristic", "Volts Characteristic = $char") } ?: run { Log.e("Frank", "Volts characteristic not found") } } ?: run { Log.e("Frank", "Service not found") } } else -> { Log.e("Frank", "Service discovery failed with status: $status") } } }
- 在
onCharacteristicRead中发起第二个特征读取
@SuppressLint("MissingPermission") override fun onCharacteristicRead(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) { super.onCharacteristicRead(gatt, characteristic, status) when (status) { BluetoothGatt.GATT_SUCCESS -> { characteristic?.let { char -> when (char.uuid) { VOLTS_UUID -> { // 处理volts数据,发射到SharedFlow val voltsValue = char.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, 0) // 示例:_bleDataFlow.emit(BleData.Volts(voltsValue)) // 读取完成后,发起amps特征的读取 val service = gatt?.getService(SERVICE_UUID) service?.let { val ampsCharacteristic = it.getCharacteristic(AMPS_UUID) ampsCharacteristic?.let { ampChar -> gatt.readCharacteristic(ampChar) Log.d("Frank - read ampsCharacteristic", "Amps Characteristic = $ampChar") } ?: run { Log.e("Frank", "Amps characteristic not found") } } } AMPS_UUID -> { // 处理amps数据,发射到SharedFlow val ampsValue = char.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, 0) // 示例:_bleDataFlow.emit(BleData.Amps(ampsValue)) } } } } else -> { Log.e("Frank", "Characteristic read failed with status: $status") } } }
额外建议
- 可以封装一个BLE操作队列,统一管理所有BLE请求(读、写、通知等),避免并发请求导致的异常。
- 读取特征前,建议校验特征的
PROPERTY_READ权限是否存在,增强代码健壮性。
内容的提问来源于stack exchange,提问作者FrankBLE
相关产品推荐
相关产品推荐

