Android 8.1关闭系统蓝牙未触发BluetoothGattCallback回调咨询
这个问题确实是Android Oreo(API 26+)之后蓝牙框架的行为变更导致的,尤其是在Google亲儿子设备(Pixel/Nexus系列)上表现得更明显,我来拆解下原因和对应的处理方案:
核心原因
从Android 8.0开始,Google对蓝牙系统框架做了针对性优化,针对系统级蓝牙关闭的场景,调整了Gatt回调的触发逻辑:
- 在API 27以下,系统关闭蓝牙时,会遍历所有活跃的Gatt连接,主动触发
onConnectionStateChange()回调并传递BluetoothProfile.STATE_DISCONNECTED状态。 - 但在API 26+(尤其是8.1的Pixel/Nexus设备),系统认为全局蓝牙关闭属于"强终止"操作,会直接终止蓝牙服务进程,跳过了Gatt层的回调通知环节。框架设计上希望App通过监听系统蓝牙状态广播来处理这类全局变更,而不是依赖Gatt连接的回调。
另外,Pixel/Nexus设备作为Google的参考设备,会严格执行官方的框架变更,部分OEM厂商的8.x设备可能因为兼容性保留了旧行为,这就造成了设备间的差异。
解决方案
要覆盖这种场景,你需要做双保险的处理,不能仅依赖BluetoothGattCallback:
监听系统蓝牙状态广播
注册BluetoothAdapter.ACTION_STATE_CHANGED广播接收器,当收到蓝牙状态变为BluetoothAdapter.STATE_OFF时,主动处理Gatt连接的清理逻辑:private BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); if (state == BluetoothAdapter.STATE_OFF) { // 主动清理Gatt连接 if (mBluetoothGatt != null) { mBluetoothGatt.close(); mBluetoothGatt = null; } // 更新本地连接状态为断开 updateConnectionState(BluetoothProfile.STATE_DISCONNECTED); } } }; // 在onCreate中注册广播 IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mBluetoothStateReceiver, filter);补充Gatt连接状态检查
可以定时调用BluetoothGatt.getConnectionState(device)来验证连接状态,如果发现状态是BluetoothProfile.STATE_DISCONNECTED但未收到回调,主动触发断开后的逻辑处理。处理Gatt对象的无效状态
当系统蓝牙关闭后,原有的BluetoothGatt对象会变成无效状态,后续调用其方法可能抛出异常,所以在广播回调里一定要及时调用close()释放资源,避免内存泄漏或异常。
额外验证
你可以测试:在系统关闭蓝牙后,你的App进程如果存活,尝试调用mBluetoothGatt.getDevice()会返回null或者触发异常,这也能侧面验证Gatt连接已经被系统强制终止,只是没有触发回调。
内容的提问来源于stack exchange,提问作者Dhananjay Suresh

