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

启用蓝牙GATT特征通知时触发ERROR_GATT_WRITE_REQUEST_BUSY的问题咨询

解决Android蓝牙GATT ERROR_GATT_WRITE_REQUEST_BUSY(201)问题

问题本质

你遇到的201错误是GATT操作串行性冲突导致的:Android蓝牙GATT客户端一次只能处理一个异步请求,而你在onServicesDiscovered回调中同时发起了writeDescriptor(CCCD写入)和requestMtu两个操作,导致GATT栈因忙碌拒绝新请求。

核心解决思路:串行化所有GATT操作

所有GATT异步操作(写入描述符、请求MTU、写入/读取特征等)必须按顺序执行,前一个操作的回调确认完成后,再发起下一个操作。

具体实现步骤

  1. 拆分onServicesDiscovered中的并行操作
    先完成CCCD写入操作,在onDescriptorWrite回调中再发起MTU请求,避免同时触发多个GATT任务。

  2. 完善回调处理逻辑
    实现onDescriptorWrite回调,确认CCCD写入成功后再执行后续操作。

  3. 调整启用通知的执行流程
    保留setCharacteristicNotification的本地启用逻辑,但将后续依赖GATT的操作(如MTU请求、特征写入)移到回调中执行。

修改后的代码示例

private class GattClientCallback : BluetoothGattCallback() {
    // ...已有代码...

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
    @SuppressLint("MissingPermission")
    override fun onServicesDiscovered(discoveredGatt: BluetoothGatt, status: Int) {
        super.onServicesDiscovered(discoveredGatt, status)
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "onServicesDiscovered: Have gatt $discoveredGatt")
            gatt = discoveredGatt
            val service = discoveredGatt.getService(SERVICE_UUID)
            service?.let {
                // 初始化各特征引用
                messageCharacteristic = it.getCharacteristic(MESSAGE_UUID)
                rreqCharacteristic = it.getCharacteristic(RREQ_UUID)
                rrepCharacteristic = it.getCharacteristic(RREP_UUID)
                
                val uuid = getUUID(it)
                notifyConnectionSuccess(gatt!!.device, uuid)
                Log.d(TAG, "UUID Discovered: $uuid")
                
                // 先触发RREP通知的CCCD写入
                rrepCharacteristic?.let { char ->
                    enableNotificationsForRrepCharacteristic(gatt, char)
                }
            }
        }
    }

    // 添加Descriptor写入完成的回调
    @SuppressLint("MissingPermission")
    override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
        super.onDescriptorWrite(gatt, descriptor, status)
        if (descriptor.uuid == CCCD_UUID && status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "CCCD写入成功,发起MTU请求")
            // CCCD操作完成后,再执行MTU请求
            gatt.requestMtu(GATT_MAX_MTU_SIZE)
        } else if (status != BluetoothGatt.GATT_SUCCESS) {
            Log.w(TAG, "CCCD写入失败,状态码:$status")
        }
    }

    // MTU修改完成的回调(可选,用于确认MTU生效后执行后续操作)
    override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
        super.onMtuChanged(gatt, mtu, status)
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "MTU设置成功:$mtu")
            // 此处可执行特征写入等后续GATT操作
        }
    }

    // ...已有代码...
}

额外注意事项

  • 所有GATT操作(包括特征写入、读取)都要遵循串行原则,禁止在同一回调或同一时间点发起多个请求。
  • 如果需要在启用通知后立即写入特征,需将写入操作放在onDescriptorWrite或onMtuChanged回调中,确保前序GATT操作已完成。
  • 检查代码中其他可能发起并发GATT操作的地方(如UI线程直接触发的写入),需统一管理操作队列,保证串行执行。

内容的提问来源于stack exchange,提问作者Harry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 12:50:15