Android多BLE外围设备连接时通知处理与数据丢失问题求解
问题根因
Android 原生 BLE 栈默认会将所有 BluetoothGattCallback 的回调事件分发到同一个共享 Binder 线程,当多台设备同时以 50Hz 频率推送数据时,单线程处理速度跟不上回调量,就会导致事件队列溢出、数据丢失,这和你是否存储独立 BluetoothGatt 实例没有关系。
可落地的解决思路
1. 回调内仅做数据转发,不处理业务逻辑
你当前在 onCharacteristicChanged 中直接执行 acc.update()、打印日志的操作都会占用回调线程的时间,多设备并发时很容易阻塞后续回调。
正确做法是拿到数据后立刻转发到异步线程处理,回调函数本身尽快返回:
// 每个TTGODevice持有专属的单线程池,避免多设备数据处理互相干扰 private final ExecutorService dataProcessExecutor = Executors.newSingleThreadExecutor(); @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { final byte[] data = characteristic.getValue(); // 直接扔到异步线程处理,立刻返回 dataProcessExecutor.execute(() -> { acc.update(data); // 业务日志也放到异步线程打 Log.d(TAG, "onCharacteristicChanged: " + Thread.currentThread().getName()); }); }
2. 为每个设备分配独立的回调线程
Android 6.0 及以上版本的 connectGatt 方法支持传入自定义 Handler,你可以为每台设备单独创建带 Looper 的线程处理回调,避免共享线程阻塞:
public class TTGODevice { private HandlerThread gattCallbackThread; private Handler gattCallbackHandler; public void connect(boolean autoConnect) { // 每个设备单独创建回调线程 gattCallbackThread = new HandlerThread("GattCallback_" + device.getAddress()); gattCallbackThread.start(); gattCallbackHandler = new Handler(gattCallbackThread.getLooper()); // 传入自定义Handler,回调就会跑在专属线程上 server = device.connectGatt(context, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE, gattCallbackHandler); } // 设备断开时记得销毁线程,避免内存泄漏 public void release() { if (server != null) { server.close(); server = null; } if (gattCallbackThread != null) { gattCallbackThread.quitSafely(); gattCallbackThread = null; gattCallbackHandler = null; } dataProcessExecutor.shutdown(); } }
3. 调整 BLE 连接参数提升传输效率
连接成功后主动申请高连接优先级,将连接间隔压到最小(通常7.5ms),足够支撑50Hz(20ms间隔)的稳定传输:
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) { // 申请高优先级连接,减少传输延迟 gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH); // 可选:申请稍大的MTU,避免偶尔的额外数据导致分包 gatt.requestMtu(32); } }
关于Fragment、ViewModel的疑问
这两个组件和你的BLE多连接丢包问题没有直接关联,他人提出该建议的核心目的是:将BLE连接逻辑放到生命周期更长的ViewModel/保留实例Fragment中,避免Activity销毁重建时触发断开重连,导致不必要的数据包丢失,你不需要优先花时间调整这部分逻辑,先优化上述BLE相关逻辑即可。
内容的提问来源于stack exchange,提问作者Ehsan Partovi
相关产品推荐
相关产品推荐

