如何重启Android BLE HID外设后重新连接至中心设备?
Android BLE HID绝对鼠标外设应用重启后稳定重连方案
问题背景
我基于开源BLE HID外设项目修改代码,实现了Android 14 Pixel 7 Pro通过BluetoothGattServer直接处理GATT特征,作为BLE绝对鼠标控制Win11笔记本。但应用重启后,没法稳定和已配对的Win11设备重连,目前只能靠两端删除配对后重新配对才能恢复功能。
试过的无效操作:
- 应用重启后直接重新广播:GATT服务器回调没成功连接通知,Win11蓝牙面板显示设备从“其他设备”转成“输入设备”但一直显示“未连接”;偶尔从Win11“设备和打印机”面板连接能成,但没法稳定复现。
- 保存配对设备地址,通过
BluetoothManager.getAdapter().getBondedDevices()或getRemoteDevice()拿已绑定的BluetoothDevice,调用gattServer.connect(device, false/true)主动连接:onConnectionStateChange()返回newState=0(断开),Win11没特征查询,但蓝牙对话框显示已连接。 - 主线程调用
gattServer.connect(),甚至用隐藏API强制TRANSPORT_LE传输,问题依旧。 - 不广播直接发起首次配对:两端弹出配对框,但Win11识别不了鼠标移动指令。
可行重连策略
1. 严格复刻GATT服务结构
应用重启重建BluetoothGattServer时,必须完全和首次配对时的GATT服务、特征、描述符结构、属性保持一致,包括UUID、权限(PERMISSION_*)、特征属性(PROPERTY_*)、描述符初始值。结构或属性有差异的话,Win11会拒绝完成GATT连接。
示例代码:
// 重启后重建HID服务,必须和首次创建时完全一致 BluetoothGattService hidService = new BluetoothGattService( UUID.fromString("00001812-0000-1000-8000-00805f9b34fb"), BluetoothGattService.SERVICE_TYPE_PRIMARY ); // 绝对鼠标的HID报告特征 BluetoothGattCharacteristic reportChar = new BluetoothGattCharacteristic( UUID.fromString("00002a4d-0000-1000-8000-00805f9b34fb"), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE ); // 添加CCCD描述符,初始值设为开启通知 BluetoothGattDescriptor cccd = new BluetoothGattDescriptor( UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"), BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE ); cccd.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); reportChar.addDescriptor(cccd); hidService.addCharacteristic(reportChar); // 别忘了添加Protocol Mode、Report Map等其他必要特征,结构要和首次一致 gattServer.addService(hidService);
2. 让Win11主动发起连接,别自己主动连
BLE HID外设的连接逻辑本来就是中央设备(Win11)主动发起,不是外设(Android)主动连。应用重启后正确流程:
- 先初始化
BluetoothGattServer,加全所有HID相关服务和特征 - 开启BLE广播,广播包必须包含HID服务UUID(
00001812-0000-1000-8000-00805f9b34fb) - 等着Win11发连接请求,别主动调用
gattServer.connect()
原因:Win11对BLE HID设备的连接管理靠自身蓝牙数据库,主动发起连接会绕过它的HID初始化流程,就算连上也识别不了HID报告。
3. 触发Win11重新同步GATT特征
如果Win11还是显示“未连接”,在onConnectionStateChange()收到连接成功(newState=2)时,主动触发特征通知的开关,强制Win11重新查询GATT特征:
@Override public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { super.onConnectionStateChange(device, status, newState); if (newState == BluetoothProfile.STATE_CONNECTED) { // 获取HID报告特征 BluetoothGattCharacteristic reportChar = gattServer.getService(UUID.fromString("00001812-0000-1000-8000-00805f9b34fb")) .getCharacteristic(UUID.fromString("00002a4d-0000-1000-8000-00805f9b34fb")); // 先关通知再打开,强制Win11重新同步 BluetoothGattDescriptor cccd = reportChar.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); cccd.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); gattServer.notifyCharacteristicChanged(device, reportChar, false); cccd.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); gattServer.notifyCharacteristicChanged(device, reportChar, false); } }
4. 重置配对信息(极端情况)
如果绑定状态显示BOND_BONDED但连不上,可能是两端配对密钥不一致。应用重启后,要是连接失败超过3次,主动触发重新配对:
// 检查绑定状态,尝试重新配对 if (device.getBondState() == BluetoothDevice.BOND_BONDED) { // 先解绑 removeBond(device); // 等解绑完成后重新发起配对 device.createBond(); } // 反射调用隐藏API实现解绑 private boolean removeBond(BluetoothDevice device) { try { Method method = device.getClass().getMethod("removeBond"); return (boolean) method.invoke(device); } catch (Exception e) { e.printStackTrace(); return false; } }
关键注意事项
- Android 14对BLE权限要求严,必须申请
BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT、BLUETOOTH_SCAN权限,还要动态授权。 - 广播包要符合BLE HID规范,必须包含HID服务UUID,广播名称得和首次配对时一样。
- 别在应用重启后立刻主动连,让Win11主导连接流程。
内容的提问来源于stack exchange,提问作者Alexander Pruss
相关产品推荐
相关产品推荐

