Android 12+ 蓝牙权限配置与使用指南
问题概述
在 Android 12 及更高版本中,Google 引入了新的蓝牙权限模型,导致许多依赖蓝牙功能的应用程序无法正常发现和连接 BLE 设备。开发者即使按照官方文档添加了新的蓝牙权限,仍然会遇到设备无法找到的问题。
核心解决方案
1. 完整的 Manifest 权限配置
在 AndroidManifest.xml
中添加以下权限声明:
xml
<!-- 传统蓝牙权限(向后兼容) -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 12+ 新增蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 可选:声明硬件特性(非必需) -->
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />
重要说明
对于 Android 13+,还需要添加 NEARBY_DEVICES 权限:
xml
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation" />
2. 运行时权限请求
Android 12+ 的蓝牙权限需要在运行时动态请求:
kotlin
private val requestMultiplePermissions =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
permissions.entries.forEach { permission ->
if (permission.value) {
// 权限已授予
} else {
// 权限被拒绝
}
}
}
fun requestBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
requestMultiplePermissions.launch(
arrayOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
)
)
} else {
// 旧版本处理逻辑
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
}
java
private static final int REQUEST_BLUETOOTH_PERMISSIONS = 100;
private void requestBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_DENIED) {
ActivityCompat.requestPermissions(this,
new String[]{
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT
},
REQUEST_BLUETOOTH_PERMISSIONS);
}
}
}
3. Jetpack Compose 实现
对于使用 Jetpack Compose 的应用:
kotlin
@Composable
fun BluetoothPermissionsHandler() {
val permissionsState = rememberMultiplePermissionsState(
permissions = listOf(
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN
)
)
// 在适当的时候请求权限
LaunchedEffect(Unit) {
if (!permissionsState.allPermissionsGranted) {
permissionsState.launchMultiplePermissionRequest()
}
}
// 检查权限状态
permissionsState.permissions.forEach { permissionState ->
when (permissionState.permission) {
Manifest.permission.BLUETOOTH_SCAN -> {
if (permissionState.status.isGranted) {
// 扫描权限已授予
}
}
Manifest.permission.BLUETOOTH_CONNECT -> {
if (permissionState.status.isGranted) {
// 连接权限已授予
}
}
}
}
}
常见问题解决
1. 无法发现蓝牙设备
如果添加了所有权限但仍然无法发现设备:
- 检查 Nearby devices 权限:在 Android 设置中,手动启用应用的"Nearby devices"权限
- 添加位置权限(针对 Android 11 及以下版本):
xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
2. 避免位置信息推断
如果应用不需要通过蓝牙扫描获取位置信息,可以声明 neverForLocation
标志:
xml
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
权限用途说明
权限 | 用途 | 是否需要运行时请求 |
---|---|---|
BLUETOOTH_SCAN | 发现和扫描蓝牙设备 | Android 12+ 需要 |
BLUETOOTH_CONNECT | 连接已配对的蓝牙设备 | Android 12+ 需要 |
BLUETOOTH_ADVERTISE | 使设备可被其他蓝牙设备发现 | Android 12+ 需要 |
ACCESS_FINE_LOCATION | 获取位置信息(Android 11-) | Android 6.0+ 需要 |
最佳实践
- 版本检查和兼容性处理:始终检查 Android 版本,为不同版本提供适当的处理逻辑
- 优雅的权限拒绝处理:当用户拒绝权限时,提供清晰的解释和引导
- 按需请求权限:只在需要时才请求相关权限,避免一次性请求所有权限
- 测试多版本兼容性:在 Android 11、12、13 等多个版本上进行测试
提示
建议使用 Android Jetpack 的 Activity Results API 来处理权限请求,而不是已弃用的 onRequestPermissionsResult
方法。
通过正确配置权限和遵循上述最佳实践,您的应用将能够在 Android 12+ 设备上正常使用蓝牙功能。