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

BLE多特征值读取异常:Kotlin Compose应用双特征通知故障

BLE特征读取异常:交替读取正常、重启后仅单特征可读的解决方案

问题根源

你的代码在onServicesDiscovered中连续调用了两次gatt.readCharacteristic(),但Android BLE的操作是串行异步的——同一时间只能处理一个BLE请求,连续发起的第二个读请求会被系统忽略或排队失败。这就是为什么只有第一个特征能稳定读取,交换UUID后偶然正常是因为设备响应顺序的巧合,重启后又回到初始状态。而nRF工具内部做了串行请求的处理,所以能正常读取两个特征。

解决方案

必须等第一个特征的读取回调onCharacteristicRead完成后,再发起第二个特征的读取请求,严格遵循BLE操作的串行规则。

修改后的代码示例

  1. 调整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")
        }
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:07:31