Skip to content

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+ 需要

最佳实践

  1. 版本检查和兼容性处理:始终检查 Android 版本,为不同版本提供适当的处理逻辑
  2. 优雅的权限拒绝处理:当用户拒绝权限时,提供清晰的解释和引导
  3. 按需请求权限:只在需要时才请求相关权限,避免一次性请求所有权限
  4. 测试多版本兼容性:在 Android 11、12、13 等多个版本上进行测试

提示

建议使用 Android Jetpack 的 Activity Results API 来处理权限请求,而不是已弃用的 onRequestPermissionsResult 方法。

通过正确配置权限和遵循上述最佳实践,您的应用将能够在 Android 12+ 设备上正常使用蓝牙功能。