Android中BLE设备断开重连异常:Nougat版本重连触发无响应求助
BLE断开后重连在Android Nougat出现ANR的解决方案
我碰到过不少开发者在Android 7.0(Nougat)及以上版本遇到BLE断开后重连触发“应用无响应”的问题,结合你的代码来看,这主要是因为Nougat对BLE连接生命周期的管控更严格,加上你的断开逻辑可能存在主线程阻塞或者状态不同步的问题。下面给你一步步拆解解决方案:
1. 把BLE断开操作移到子线程执行
BLE相关的GATT操作(包括断开)绝对不能放在主线程里跑,Nougat开始系统对主线程耗时操作的阈值卡得更严,一旦disconnect里有阻塞操作,直接就会触发ANR。
修改你的mBTLE_Service里的disconnect方法,把所有GATT操作封装到子线程:
public void disconnect() { new Thread(new Runnable() { @Override public void run() { if (mBluetoothGatt != null) { // 先关闭所有特征通知,避免残留回调 for (BluetoothGattService service : mBluetoothGatt.getServices()) { for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) { mBluetoothGatt.setCharacteristicNotification(characteristic, false); } } } // 先断开GATT连接,再关闭资源 mBluetoothGatt.disconnect(); mBluetoothGatt.close(); mBluetoothGatt = null; } } }).start(); }
2. 等待断开完成的回调,再执行重连
别着急在调用disconnect后立刻发起重连,必须等系统返回断开成功的回调。你需要在BluetoothGattCallback里监听连接状态变化:
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothGatt.STATE_DISCONNECTED) { // 标记设备已完全断开,这里可以发广播通知Activity Intent disconnectIntent = new Intent("BLE_DEVICE_DISCONNECTED"); sendBroadcast(disconnectIntent); // 如果需要自动重连,也在这里触发,不要在其他地方直接调用重连 // reconnectDevice(); } }
然后在你的Activity里注册广播接收器,收到断开完成的通知后再处理重连或者页面操作,避免状态冲突。
3. 优化你的onBackPressed对话框逻辑
用户点击“OK”后,最好给个加载提示,让用户知道正在处理,同时等待断开完成再关闭对话框或结束Activity:
@Override public void onBackPressed() { AlertDialog.Builder adb = new AlertDialog.Builder(ActivityName.this); adb.setTitle("DISCONNECT"); adb.setCancelable(true); adb.setMessage("Do you want to disconnect"); adb.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 显示断开中的加载提示 ProgressDialog progressDialog = ProgressDialog.show(ActivityName.this, "", "Disconnecting...", true); // 临时注册广播接收器监听断开完成 BroadcastReceiver disconnectReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { progressDialog.dismiss(); unregisterReceiver(this); dialog.dismiss(); // 这里可以安全地结束Activity或者执行其他操作 finish(); } }; registerReceiver(disconnectReceiver, new IntentFilter("BLE_DEVICE_DISCONNECTED")); // 发起断开操作 mBTLE_Service.disconnect(); } }); adb.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); AlertDialog alertDialog = adb.create(); alertDialog.show(); }
4. 检查Nougat的BLE权限要求
Nougat开始BLE扫描需要位置权限,确保你的应用已经声明并请求了相关权限:
在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 针对Android 12及以上的权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
并且在运行时动态请求ACCESS_FINE_LOCATION权限,否则BLE操作会静默失败,间接导致ANR。
总结一下:核心就是避免主线程执行BLE操作、确保断开状态同步后再重连、适配系统的权限要求,这几点搞定后Nougat上的ANR问题应该就能解决了。
内容的提问来源于stack exchange,提问作者Bichitra Pradhan
相关产品推荐
相关产品推荐

