Android BLE延迟写入onExecuteWrite的execute置真及多包传输问题问询
BLE图片传输问题:可靠写入流程异常与ERROR_GATT_WRITE_REQUEST_BUSY错误
问题现象
- 拆分图片数据循环调用
gatt.writeCharacteristic()时,首次写入返回**ERROR_GATT_WRITE_REQUEST_BUSY(201)**错误,但外设实际已收到数据。 - 外设端
onCharacteristicWriteRequest中preparedWrite为true,数据缓存正常,但后续onExecuteWrite的execute参数始终为false,无法触发最终数据合并处理。 - 已通过同步队列控制写入流程,避免并发写请求。
客户端代码
@RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun sendAlbumArt(gatt: BluetoothGatt, characteristic:BluetoothGattCharacteristic, albumByteArray: ByteArray) { Log.e("sendAlbumArt-", "inside sendAlbumArt") Log.e("sendAlbumArt-", "The mtuValue allowed is $mtuValue") val times = ceil(albumByteArray.size / mtuValue.toDouble()).toInt() Log.i("sendAlbumArt", "NUMBER OF BLE PACKETS: $times"); //writeDataSynchronized(gatt, characteristic, albumByteArray) var start = 0 var last = 0 //Thread.sleep(1000) for (i in 0 until times) { Log.i("sendAlbumArt-", "Inside the for loop to send album art data") var end = start + mtuValue if (end > albumByteArray.size) { end = albumByteArray.size } val packet: ByteArray = albumByteArray.copyOfRange(start, end) writeDataSynchronized(gatt, characteristic, packet) start += mtuValue last++ } if (last == times){ writeDataSynchronized(gatt, characteristic, ALBUM_ART_STOP) Log.i("sendAlbumArt-", "last data is written: ${ALBUM_ART_STOP.toHexString()}") } /*val executeReliableWrite = gatt.executeReliableWrite() if (executeReliableWrite){ Log.i("sendAlbumArt-", "executeReliableWrite: $executeReliableWrite") }else{ Log.i("sendAlbumArt-", "executeReliableWrite: $executeReliableWrite") }*/ val albumArt = BitmapFactory.decodeByteArray(albumByteArray, 0, albumByteArray.size) binding.albumartIv.setImageBitmap(albumArt) } @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun writeDataSynchronized( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray ) { Log.e("writeDataSynchronized-", "inside writeDataSynchronized") Log.e("writeDataSynchronized-", "Queue size: ${writeQueue.size}") synchronized(writeLock) { writeQueue.add(data) if (!isWriting) { Log.e("writeDataSynchronized-", "Queue size: ${writeQueue.size}") writeNextData(gatt, characteristic) } } } @RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun writeNextData(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { Log.e("writeNextData-", "inside writeNextData") synchronized(writeLock) { if (!writeQueue.isEmpty()) { isWriting = true val nextData = writeQueue.poll() Log.e("writeNextData-", "data to send to peripheral is: ${nextData.toHexString()}") gatt.writeCharacteristic( characteristic, nextData ?: "".toByteArray(), BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT ) } else { isWriting = false } } }
外设端接收数据代码
override fun onCharacteristicWriteRequest( device: BluetoothDevice?, requestId: Int, characteristic: BluetoothGattCharacteristic?, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray? ) { if (!preparedWrite){ //do something for immediate write }else{ if (characteristic != null) { Log.e("onCharacteristicWriteRequest", "characteristic is: $characteristic") Log.e( "onCharacteristicWriteRequest", "characteristic is: ${characteristic.uuid}" ) if (characteristic.uuid == albumArtCharacteristicUuid) { Log.i( "onCharacteristicWriteRequest-AlbumArt", "Album art characteristic value is${value?.toHexString()}" ) if (value != null) { //handleIncomingAlbumArtData(value) fillTheBuffer(value) } } } } if (responseNeeded) { bluetoothGattServer.sendResponse( device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null ) } } override fun onExecuteWrite(device: BluetoothDevice?, requestId: Int, execute: Boolean) { super.onExecuteWrite(device, requestId, execute) Log.e("onExecuteWrite", "execute: $execute") if (execute){ handleIncomingAlbumArtData() } bluetoothGattServer.sendResponse( device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null ) Log.e("onExecuteWrite", "handleIncomingAlbumArtData is not executed successfully") } private fun fillTheBuffer(value: ByteArray) { Log.e("fillTheBuffer", "Inside the method") if (value.contentEquals(ALBUM_ART_STOP)){ Log.e("fillTheBuffer", "Last Frame") Log.e("fillTheBuffer_ALBUM_ART_STOP", "buffer Size is: ${buffer.size}") }else{ //buffer.addAll(value.toList()) buffer.addAll(value.toList()) Log.e("fillTheBuffer", "buffer Size is: ${buffer.size}") } }
问题分析与解决方案
1. 可靠写入流程纠正
BLE可靠写入(延迟写入)的完整流程是:
- 客户端先调用
gatt.beginReliableWrite()开启会话 - 多次调用
gatt.writeCharacteristic()写入分片 - 最后调用
gatt.executeReliableWrite()提交,触发外设onExecuteWrite(execute=true) - 中途取消则调用
gatt.abortReliableWrite(),外设onExecuteWrite(execute=false)
你的代码未调用beginReliableWrite()和executeReliableWrite(),导致设备进入半可靠状态,最终触发execute=false。
2. 客户端代码修复
修改sendAlbumArt方法,补全可靠写入流程:
@RequiresApi(Build.VERSION_CODES.TIRAMISU) private fun sendAlbumArt(gatt: BluetoothGatt, characteristic:BluetoothGattCharacteristic, albumByteArray: ByteArray) { Log.e("sendAlbumArt-", "inside sendAlbumArt") Log.e("sendAlbumArt-", "The mtuValue allowed is $mtuValue") val times = ceil(albumByteArray.size / mtuValue.toDouble()).toInt() Log.i("sendAlbumArt", "NUMBER OF BLE PACKETS: $times"); // 开启可靠写入会话 if (!gatt.beginReliableWrite()) { Log.e("sendAlbumArt", "Failed to begin reliable write") return } var start = 0 for (i in 0 until times) { Log.i("sendAlbumArt-", "Inside the for loop to send album art data") var end = start + mtuValue end = if (end > albumByteArray.size) albumByteArray.size else end val packet: ByteArray = albumByteArray.copyOfRange(start, end) // 写入分片 val writeSuccess = gatt.writeCharacteristic( characteristic, packet, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT ) if (!writeSuccess) { Log.e("sendAlbumArt", "Write packet $i failed") gatt.abortReliableWrite() return } start += mtuValue } // 写入结束标记 val stopWriteSuccess = gatt.writeCharacteristic( characteristic, ALBUM_ART_STOP, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT ) if (!stopWriteSuccess) { Log.e("sendAlbumArt", "Write stop marker failed") gatt.abortReliableWrite() return } // 提交所有写入,触发外设onExecuteWrite(execute=true) val executeSuccess = gatt.executeReliableWrite() Log.i("sendAlbumArt", "executeReliableWrite: $executeSuccess") val albumArt = BitmapFactory.decodeByteArray(albumByteArray, 0, albumByteArray.size) binding.albumartIv.setImageBitmap(albumArt) }
3. 外设端代码优化
在onExecuteWrite中处理缓存数据并清理:
override fun onExecuteWrite(device: BluetoothDevice?, requestId: Int, execute: Boolean) { super.onExecuteWrite(device, requestId, execute) Log.e("onExecuteWrite", "execute: $execute") if (execute) { // 检查结束标记并处理数据 if (buffer.size >= ALBUM_ART_STOP.size && buffer.takeLast(ALBUM_ART_STOP.size).contentEquals(ALBUM_ART_STOP)) { val finalData = buffer.dropLast(ALBUM_ART_STOP.size).toByteArray() handleIncomingAlbumArtData(finalData) Log.e("onExecuteWrite", "handleIncomingAlbumArtData executed successfully") } else { Log.e("onExecuteWrite", "Missing stop marker, discard data") } } // 清空缓存 buffer.clear() bluetoothGattServer.sendResponse( device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null ) }
4. 解决ERROR_GATT_WRITE_REQUEST_BUSY错误
该错误源于上一次写入未完成就发起新请求,可通过客户端BluetoothGattCallback的onCharacteristicWrite回调控制写入顺序:
override fun onCharacteristicWrite( gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int ) { super.onCharacteristicWrite(gatt, characteristic, status) if (status == BluetoothGatt.GATT_SUCCESS) { // 继续写入下一个分片 writeNextData(gatt!!, characteristic!!) } else { Log.e("GattCallback", "Write failed with status: $status") gatt?.abortReliableWrite() } }
内容的提问来源于stack exchange,提问作者Daredevil
相关产品推荐
相关产品推荐

