Android BLE应用RSSI读取40分钟后触发DeadObjectException问题
问题分析与解决方案
兄弟,我之前碰到过几乎一模一样的问题!先给你拆解核心问题,再给你落地的解决办法:
核心问题拆解
DeadObjectException的本质:你的mBluetoothGatt对象虽然不为空,但它内部绑定的蓝牙服务Binder代理已经失效了——系统可能因为长时间频繁的RSSI调用、资源回收或者蓝牙服务临时重启,切断了这个跨进程连接。- 为什么try-catch没触发:
readRemoteRssi()是异步跨进程调用,DeadObjectException是在系统蓝牙服务的Binder线程里抛出的,根本不会回到你调用的线程的try块中,所以你的catch完全抓不到这个异常! - RSSI调用的恶性循环:你原来的逻辑是不管上一次RSSI读取成功与否,都会每隔1秒发一次请求,一旦Binder失效,就会不断堆积失败的调用,进一步加剧系统蓝牙服务的压力,最终导致整个Gatt连接彻底失效,数据停止更新。
具体解决方案
1. 新增连接状态追踪,别只靠mBluetoothGatt != null
首先在你的类里加几个状态变量,用来追踪连接状态和重试次数:
// 记录当前蓝牙连接状态 private int mConnectionState = BluetoothProfile.STATE_DISCONNECTED; // RSSI读取最大重试次数 private static final int MAX_RSSI_RETRY = 3; private int mRssiRetryCount = 0;
然后重写BluetoothGattCallback的连接状态回调,实时更新状态:
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); mConnectionState = newState; if (newState == BluetoothProfile.STATE_DISCONNECTED) { // 连接断开时,主动释放Gatt资源,避免无效对象占着坑 if (mBluetoothGatt != null) { mBluetoothGatt.close(); mBluetoothGatt = null; } // 这里写你的重新连接逻辑,比如调用设备的connect方法 } }
2. 改成“回调驱动”的RSSI调用逻辑
原来的无条件循环调用是大坑,改成只有上一次RSSI读取成功后,才发送下一次请求,避免无效请求堆积:
首先删掉原来的checkRssi()方法,然后重写onReadRemoteRssi回调:
@Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); if (status == BluetoothGatt.GATT_SUCCESS) { // RSSI读取成功:重置重试计数,发送下一次请求 mRssiRetryCount = 0; bleHandler.sendEmptyMessageDelayed(MSG_CHECK_RSSI, 5000); // 5秒间隔比1秒更稳妥,减少系统压力 // 这里处理你的RSSI数据,更新UI/图表等逻辑 } else { // 读取失败:增加重试计数,触发重试逻辑 mRssiRetryCount++; handleRssiRetry(); } } // 处理RSSI读取失败的重试逻辑 private void handleRssiRetry() { if (mRssiRetryCount < MAX_RSSI_RETRY) { // 还在重试次数内,1秒后重试 bleHandler.sendEmptyMessageDelayed(MSG_CHECK_RSSI, 1000); } else { // 重试次数耗尽,触发重新连接 mRssiRetryCount = 0; if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); // 延迟3秒后尝试重新连接,给系统一点缓冲时间 bleHandler.postDelayed(() -> { if (mBluetoothGatt != null) { mBluetoothGatt.connect(); } }, 3000); } } }
3. 修正Handler里的RSSI触发逻辑
现在Handler里只需要做状态检查,然后发起读取:
case MSG_CHECK_RSSI: Log.i(HANDLER_TAG, "MSG_CHECK_RSSI"); // 先检查连接状态是否正常,再发起读取 if (mBluetoothGatt != null && mConnectionState == BluetoothProfile.STATE_CONNECTED) { try { mBluetoothGatt.readRemoteRssi(); } catch (RuntimeException e) { // 兜底捕获可能的RuntimeException,虽然之前没触发,但加上更保险 e.printStackTrace(); mRssiRetryCount++; handleRssiRetry(); } } else { // 连接状态异常,直接触发重试/重连 mRssiRetryCount++; handleRssiRetry(); } break;
4. 生命周期里正确释放资源
在Activity/Fragment销毁时,一定要清理Gatt和Handler资源,避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); // 关闭Gatt连接 if (mBluetoothGatt != null) { mBluetoothGatt.close(); mBluetoothGatt = null; } // 移除Handler所有未执行的消息 if (bleHandler != null) { bleHandler.removeCallbacksAndMessages(null); } }
额外排查要点
- 可以监听系统蓝牙状态变化,比如注册
BluetoothAdapter.ACTION_STATE_CHANGED广播,当蓝牙重启时,主动触发重连逻辑 - 检查BLE设备本身的固件,有些设备长时间连续响应RSSI请求会进入休眠或者拒绝响应
内容的提问来源于stack exchange,提问作者Geeky bean
相关产品推荐
相关产品推荐

