You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 12迁移至API31获取配对蓝牙设备时BLUETOOTH_CONNECT权限异常

解决Android 31获取配对蓝牙设备的权限问题

你遇到的SecurityException核心原因是:Android 12(API 31)开始,BLUETOOTH_CONNECT属于危险权限,仅在Manifest中声明是不够的,必须动态向用户申请权限。

问题分析

API 31对蓝牙权限做了明确拆分:

  • BLUETOOTH、BLUETOOTH_ADMIN变为普通权限,安装时自动授予;
  • BLUETOOTH_CONNECT、BLUETOOTH_SCAN是危险权限,必须通过代码主动申请用户授权,否则调用bondedDevices、蓝牙连接等核心操作会直接抛出权限异常。

解决步骤

1. 动态申请BLUETOOTH_CONNECT权限

在调用bluetoothAdapter?.bondedDevices之前,先完成权限检查与申请:

private val REQUEST_BLUETOOTH_CONNECT = 1

// 检查并申请蓝牙连接权限
private fun checkBluetoothConnectPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.BLUETOOTH_CONNECT),
                REQUEST_BLUETOOTH_CONNECT
            )
            return
        }
        // 权限已授予,执行获取配对设备逻辑
        getPairedBluetoothDevice()
    } else {
        // API 30及以下无需动态申请,直接执行逻辑
        getPairedBluetoothDevice()
    }
}

// 处理权限申请结果
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == REQUEST_BLUETOOTH_CONNECT) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 用户授权成功,执行设备获取逻辑
            getPairedBluetoothDevice()
        } else {
            // 用户拒绝权限,提示无法操作
            Toast.makeText(this, "需要蓝牙连接权限才能访问配对设备", Toast.LENGTH_SHORT).show()
        }
    }
}

// 封装你的原蓝牙设备处理逻辑
private fun getPairedBluetoothDevice() {
    val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices

    pairedDevices?.forEach { device ->
        if(device.address.lowercase() == configHelper.bluetoothAddress.lowercase()){
            bluetoothDevice = device
            paymentTerminalName = device.name
            paymentTerminalMAC = device.address // MAC地址
            UUIDFromPaymentTerminal = device.uuids[0].uuid // 服务UUID
        }
    }
    if(bluetoothDevice == null){
        // 支付终端未配对
        throw Exception("支付终端未配对")
    }
    socket = bluetoothDevice!!.createRfcommSocketToServiceRecord(UUIDFromPaymentTerminal)
    // 取消扫描,避免拖慢连接速度
    bluetoothAdapter!!.cancelDiscovery()
    (socket!! as BluetoothSocket).connect()
}

2. Manifest权限适配(可选优化)

为了更严谨,可给权限添加API级别限制,避免低版本系统出现无效权限声明:

<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_CONNECT" android:minSdkVersion="31" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:minSdkVersion="31" />

额外注意事项

  • 调用device.uuids时同样依赖BLUETOOTH_CONNECT权限,确保权限申请通过后再执行该逻辑;
  • 若应用需要扫描蓝牙设备,BLUETOOTH_SCAN权限也需动态申请,但当前问题核心是BLUETOOTH_CONNECT;
  • 建议提前申请权限,比如进入蓝牙功能页面时就触发申请,不要等到调用设备列表时才弹出授权框。

内容的提问来源于stack exchange,提问作者Kalai Selvi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:40:58