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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 18:44:51