使用D11打印机蓝牙连接时遇BLUETOOTH_SCAN权限报错求助
解决Android蓝牙扫描报错:Need android.permission.BLUETOOTH_SCAN permission for AttributionSource
问题原因
你遇到的报错是因为Android 12(API 31)及以上版本对蓝牙权限做了更严格的拆分,即使在Manifest中添加了权限,也必须在运行时主动申请危险权限,且isDiscovering、cancelDiscovery、startDiscovery这些操作都需要持有BLUETOOTH_SCAN权限,仅靠@SuppressLint("MissingPermission")注解无法绕过系统的权限检查。
解决方案
1. 确认Manifest权限配置
在AndroidManifest.xml中添加对应API级别的权限:
<!-- Android 12+ 所需权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 若无需通过蓝牙扫描关联位置,可添加此flag避免位置权限请求 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <!-- Android 11及以下 所需权限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 声明蓝牙硬件需求 --> <uses-feature android:name="android.hardware.bluetooth" android:required="true" />
2. 添加运行时权限申请逻辑
执行蓝牙扫描前,必须检查并申请对应权限:
// 根据系统版本定义需要申请的权限数组 private val requiredPermissions: Array<String> get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT) } else { arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.BLUETOOTH_ADMIN) } // 检查所有权限是否已授予 private fun hasBluetoothPermissions(): Boolean { return requiredPermissions.all { ContextCompat.checkSelfPermission(requireActivity(), it) == PackageManager.PERMISSION_GRANTED } } // 发起权限申请 private fun requestBluetoothPermissions() { registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> val allGranted = permissions.values.all { it } if (allGranted) { searchForPrintersInternal() } else { AlertsUtil.showBaseOkWarningDialog( requireActivity(), getString(R.string.permission_required), getString(R.string.bluetooth_permission_denied) ) } }.launch(requiredPermissions) }
3. 修改扫描方法,先做权限校验
将原方法拆分为权限校验和实际扫描逻辑,移除无效的权限抑制注解:
// 对外调用的入口方法,先做权限检查 private fun searchForPrinters() { if (!hasBluetoothPermissions()) { requestBluetoothPermissions() return } searchForPrintersInternal() } // 实际执行蓝牙扫描的逻辑(确保权限已持有) private fun searchForPrintersInternal() { val bluetoothManager = requireActivity().getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager bluetoothAdapter = bluetoothManager.adapter if (bluetoothAdapter == null) { AlertsUtil.showBaseOkWarningDialog( requireActivity(), getString(R.string.bluetooth_not_available), "" ) return } else if (!bluetoothAdapter!!.isEnabled) { askToEnableBluetooth() return } if (bluetoothAdapter?.isDiscovering == true) { Logger.log(TAG, "cancel start discovery") bluetoothAdapter?.cancelDiscovery() } initBluetoothScanBroadcastReceiver() bluetoothAdapter?.startDiscovery() }
关键注意点
- 不要依赖
@SuppressLint("MissingPermission")跳过权限检查,该注解仅抑制编译器警告,系统运行时仍会校验权限。 - Android 12+中
BLUETOOTH_SCAN和BLUETOOTH_CONNECT是独立的危险权限,需分别申请。 - 若无需通过蓝牙扫描获取位置信息,务必给
BLUETOOTH_SCAN添加neverForLocationflag,避免不必要的位置权限请求。
内容的提问来源于stack exchange,提问作者LMB
相关产品推荐
相关产品推荐

