Android与ESP32 BLE连续读写仅首个回调触发问题求助
问题分析与解决方案
核心问题原因
Android的BLE GATT操作是异步且串行执行的,GATT客户端不会同时处理多个并发的读写请求。如果在第一个操作(比如写特征)的回调确认完成前就发起第二个操作(读特征),第二个请求会被GATT栈直接丢弃,导致ESP32端收不到第二个操作的请求,自然不会触发对应的回调。
你当前代码里加的TimeUnit.MILLISECONDS.sleep(500)只是单纯阻塞线程,无法保证第一个BLE操作真正完成——BLE操作的完成是通过BluetoothGattCallback的回调通知的,sleep和操作是否完成没有关联。
修复方案:基于回调串行化BLE操作
修改Android端代码,利用BluetoothGattCallback的回调函数触发下一个操作,确保前一个操作完全完成后再执行后续步骤。
步骤1:完善BluetoothGattCallback回调实现
重写onCharacteristicWrite和onCharacteristicRead回调,感知操作完成状态:
override fun onCharacteristicWrite( gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int ) { super.onCharacteristicWrite(gatt, characteristic, status) if (status == BluetoothGatt.GATT_SUCCESS) { // 写操作成功后,触发读取站点的操作 if (characteristic?.uuid == SET_TIME_UUID) { getStations() } } else { Log.e(TAG, "写特征失败,状态码:$status") } } override fun onCharacteristicRead( gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int ) { super.onCharacteristicRead(gatt, characteristic, status) if (status == BluetoothGatt.GATT_SUCCESS) { val value = characteristic?.value?.let { String(it) } Log.d(TAG, "读取到特征值:$value") // 这里可以处理读取到的站点数据 } else { Log.e(TAG, "读特征失败,状态码:$status") } }
步骤2:修改sync函数,仅发起第一个操作
去掉连续调用的逻辑,只启动第一个写操作,后续操作由回调触发:
fun sync() { Log.d("sync", "syncing") bluetoothGatt?.apply { if (device.bondState != BluetoothDevice.BOND_BONDED) { device.createBond() } else { // 只发起写时间操作,读站点操作在写成功回调中触发 updateTime() } } }
步骤3:移除sendToCharacteristic中的无用sleep
现在用回调控制操作顺序,无需阻塞线程:
private fun sendToCharacteristic(characteristic: BluetoothGattCharacteristic?, value: String) { Log.d(TAG, "Sending to write characteristic ${characteristic?.uuid} value ($value)") characteristic?.value = value.toByteArray() bluetoothGatt?.writeCharacteristic(characteristic) }
额外注意事项
- 确保
BluetoothGattCallback的回调在主线程执行,避免线程安全问题。 - 如果需要执行多个连续BLE操作,可以封装操作队列,每次回调完成后取出下一个操作执行,提升扩展性。
- ESP32端的NimBLE代码无需修改,单独操作正常说明问题根源在Android端的并发操作处理。
内容的提问来源于stack exchange,提问作者La bla bla
相关产品推荐
相关产品推荐

