启用蓝牙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、写入/读取特征等)必须按顺序执行,前一个操作的回调确认完成后,再发起下一个操作。
具体实现步骤
拆分
onServicesDiscovered中的并行操作
先完成CCCD写入操作,在onDescriptorWrite回调中再发起MTU请求,避免同时触发多个GATT任务。完善回调处理逻辑
实现onDescriptorWrite回调,确认CCCD写入成功后再执行后续操作。调整启用通知的执行流程
保留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
相关产品推荐
相关产品推荐

