Android 13启动时已连蓝牙耳机触发BLUETOOTH_CONNECT权限异常求助
问题分析
从异常信息和场景来看,核心问题是Android 13下,应用启动时已连接蓝牙耳机的场景中,在权限未完成授予前就调用了蓝牙相关API,触发了BLUETOOTH_CONNECT权限检查失败。
具体原因:
- 你的权限请求逻辑存在错误:判断条件使用
&&,意味着只有当BLUETOOTH_SCAN和BLUETOOTH_CONNECT两个权限都未授予时才发起请求,只要其中一个已授予就会跳过请求,可能导致权限缺失。 - 应用启动阶段(比如
LoginActivity的onCreate)在权限请求完成前,就执行了访问蓝牙设备的操作(比如调用getConnectedDevicesWithAttribution),此时权限尚未授予,直接触发SecurityException。 - Android 13中
BLUETOOTH_CONNECT属于危险权限,必须用户主动授予后才能调用蓝牙连接类API,低版本系统权限机制不同所以无此问题。
解决方案
1. 修正清单文件权限配置
为旧版本权限添加maxSdkVersion,避免Android 13及以上系统冗余处理:
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
2. 修复权限请求逻辑
调整权限数组
Android 13不需要ACCESS_FINE_LOCATION(已通过neverForLocation标记跳过位置关联),也不需要旧的BLUETOOTH/BLUETOOTH_ADMIN权限:
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU) private static final String[] PERMISSIONS_BLUETOOTH_33 = { Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT }; private static final String[] PERMISSIONS_BLUETOOTH = { Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_ADMIN }; private String[] getBluetoothPermissions() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ? PERMISSIONS_BLUETOOTH_33 : PERMISSIONS_BLUETOOTH; }
修正权限检查逻辑
将判断条件从&&改为||,只要任一权限未授予就发起请求,并将蓝牙操作延迟到权限授予后执行:
private void checkPermissions() { boolean hasScan = ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED; boolean hasConnect = ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED; if (!hasScan || !hasConnect) { ActivityCompat.requestPermissions(this, getBluetoothPermissions(), 1); } else { // 权限已授予,执行蓝牙初始化逻辑 initBluetoothOperations(); } } // 处理权限请求结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { initBluetoothOperations(); } else { Toast.makeText(this, "蓝牙权限未授予,无法使用蓝牙耳机功能", Toast.LENGTH_SHORT).show(); // 可添加权限引导逻辑 } } } // 所有蓝牙相关操作统一放到这里执行 private void initBluetoothOperations() { // 示例:获取已连接的蓝牙耳机 BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); List<BluetoothDevice> connectedHeadphones = bluetoothManager.getConnectedDevices(BluetoothProfile.HEADSET); // 后续业务逻辑处理 }
3. 关键注意事项
- 确保所有蓝牙相关操作(包括获取已连接设备、绑定蓝牙服务等)都在
initBluetoothOperations中执行,禁止在权限授予前调用任何蓝牙API。 - 检查
Application类或其他早期生命周期组件,避免在这些地方提前执行蓝牙操作。
内容的提问来源于stack exchange,提问作者DealZg
相关产品推荐
相关产品推荐

