Android 13 BLE扫描异常:权限弹窗未弹出且BluetoothLeScanner为空
问题分析与解决方案
一、权限请求弹窗不出现的原因
- 重复请求码导致请求被覆盖:你当前在
getPermission方法中重复使用相同的requestCode=2发起权限请求,Android系统同一时间仅能处理一个权限请求,后续请求会被直接忽略,最终仅会弹出第一个权限(BLUETOOTH_SCAN)的弹窗,其余权限请求根本不会触发。 - 权限请求方式错误:应将所有需要动态申请的危险权限打包成一个数组,一次性发起请求,而非逐个调用。
- Android 12+ BLE权限适配缺失:
BLUETOOTH_SCAN和BLUETOOTH_CONNECT是API 31(Android 12)新增的权限,需在清单文件补充配置:- 若扫描无需关联位置信息,必须给
BLUETOOTH_SCAN加上android:usesPermissionFlags="neverForLocation",否则系统会强制要求位置权限,且可能打乱权限请求逻辑。 - 若
targetSdkVersion < 31,新权限不生效,需使用旧版BLUETOOTH和BLUETOOTH_ADMIN权限(这两个在API 31前属于普通权限,无需动态请求)。
- 若扫描无需关联位置信息,必须给
- 位置权限适配问题:若
targetSdkVersion >= 29(Android 10),ACCESS_COARSE_LOCATION属于危险权限,但如果已在系统设置中授权过,再次请求不会弹出弹窗;Android 12+若配置了neverForLocation,则无需请求位置权限。
二、缺失权限是否会导致BluetoothLeScanner为空?
不会。BluetoothLeScanner为空的常见原因:
- 设备本身不支持BLE功能(部分老旧设备无BLE模块);
- 蓝牙未开启(
bluetoothManager.adapter会为空,或蓝牙处于关闭状态); - 系统蓝牙服务异常(极少数情况)。
权限缺失只会导致扫描操作失败(触发onScanFailed回调,错误码通常为SCAN_FAILED_PERMISSION_DENIED),但不会导致BluetoothLeScanner实例为空。
修正后的代码示例
1. 清单文件补充配置(适配Android 12+)
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 仅当需要关联位置的BLE扫描时保留,否则可移除 --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 声明应用依赖BLE功能,过滤不支持的设备 --> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
2. 修正后的Activity代码
class MainActivity : AppCompatActivity() { private val REQUEST_PERMISSIONS_CODE = 1001 private val ENABLE_BLUETOOTH_CODE = 1002 // 根据Android版本动态选择需要请求的权限 private val REQUIRED_PERMISSIONS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT) } else { arrayOf(Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION) } private var bluetoothLeScanner: BluetoothLeScanner? = null private val bleScanner = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult?) { super.onScanResult(callbackType, result) result?.let { Log.d("MainActivity", "设备地址:${it.device.address},名称:${it.device.name ?: "未知"}") } } override fun onScanFailed(errorCode: Int) { super.onScanFailed(errorCode) Log.e("MainActivity", "扫描失败,错误码:$errorCode") } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val fab: View = findViewById(R.id.fab) fab.setOnClickListener { bluetoothLeScanner?.startScan(bleScanner) ?: Log.e("MainActivity", "BLE扫描器未初始化,请检查蓝牙状态") } } override fun onStart() { super.onStart() initBluetoothLeScanner() requestRequiredPermissions() } override fun onStop() { bluetoothLeScanner?.stopScan(bleScanner) super.onStop() } private fun initBluetoothLeScanner() { val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager val bluetoothAdapter = bluetoothManager.adapter ?: run { Log.e("MainActivity", "设备不支持蓝牙") return } // 检查蓝牙是否开启,未开启则跳转至设置页面 if (!bluetoothAdapter.isEnabled) { startActivityForResult(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), ENABLE_BLUETOOTH_CODE) return } bluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner } private fun requestRequiredPermissions() { val permissionsToRequest = mutableListOf<String>() REQUIRED_PERMISSIONS.forEach { perm -> if (ActivityCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) { permissionsToRequest.add(perm) } } if (permissionsToRequest.isNotEmpty()) { ActivityCompat.requestPermissions(this, permissionsToRequest.toTypedArray(), REQUEST_PERMISSIONS_CODE) } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_PERMISSIONS_CODE) { val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (!allGranted) { Log.w("MainActivity", "部分权限未授权,可能无法正常扫描BLE设备") } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == ENABLE_BLUETOOTH_CODE) { if (resultCode == RESULT_OK) { initBluetoothLeScanner() } else { Log.e("MainActivity", "蓝牙未开启,无法扫描BLE设备") } } } }
内容的提问来源于stack exchange,提问作者Boris
相关产品推荐
相关产品推荐

