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

Android 12及以上版本蓝牙设备发现功能失效问题求助

Android 12 蓝牙设备发现适配解决方案

核心问题定位

Android 12(API 31)对蓝牙扫描的权限体系、广播接收机制做了针对性限制,即便bluetoothAdapter.startDiscovery()执行成功,也会因权限缺失或广播注册方式错误导致无法接收设备发现事件。


一、权限适配(必做)

除旧版本的BLUETOOTH、BLUETOOTH_ADMIN权限外,Android 12+需补充以下权限:

  • BLUETOOTH_SCAN:蓝牙扫描专属权限,必须运行时申请
  • ACCESS_FINE_LOCATION:扫描非配对设备时必须申请(系统将蓝牙扫描结果与位置信息绑定)

权限声明(AndroidManifest.xml)

<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_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />

运行时权限申请代码

private val REQUEST_BLUETOOTH_PERM = 1001

private fun checkAndRequestPermissions() {
    val requiredPermissions = arrayOf(
        Manifest.permission.BLUETOOTH_SCAN,
        Manifest.permission.ACCESS_FINE_LOCATION
    )
    val needRequest = requiredPermissions.any {
        ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
    }
    if (needRequest) {
        ActivityCompat.requestPermissions(this, requiredPermissions, REQUEST_BLUETOOTH_PERM)
    } else {
        startDiscovery()
    }
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == REQUEST_BLUETOOTH_PERM && grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
        startDiscovery()
    }
}

二、BroadcastReceiver 注册方式修正

Android 12+ 禁止通过Manifest静态注册接收BluetoothDevice.ACTION_FOUND这类隐式广播,必须动态注册。

错误示例(Manifest注册,Android12+无效)

<!-- 此方式在Android12及以上版本无法接收设备发现事件 -->
<receiver android:name=".BluetoothReceiver">
    <intent-filter>
        <action android:name="android.bluetooth.device.action.FOUND" />
    </intent-filter>
</receiver>

正确示例(动态注册)

private lateinit var discoveryReceiver: BroadcastReceiver

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // 初始化广播接收器
    discoveryReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            intent ?: return
            when(intent.action) {
                BluetoothDevice.ACTION_FOUND -> {
                    // 获取发现的蓝牙设备
                    val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
                    device?.let {
                        Log.d("BluetoothTest", "发现设备:${it.name ?: "未知设备"} | ${it.address}")
                        // 此处可将设备添加至UI列表
                    }
                }
                BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                    Log.d("BluetoothTest", "扫描结束")
                }
            }
        }
    }

    // 注册广播过滤器
    val filter = IntentFilter().apply {
        addAction(BluetoothDevice.ACTION_FOUND)
        addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
    }
    registerReceiver(discoveryReceiver, filter)
}

override fun onDestroy() {
    super.onDestroy()
    // 必须注销广播,避免内存泄漏
    unregisterReceiver(discoveryReceiver)
    // 销毁时停止扫描
    if (bluetoothAdapter.isDiscovering) {
        bluetoothAdapter.cancelDiscovery()
    }
}

三、完整适配示例代码

主Activity核心逻辑

class BluetoothScanActivity : AppCompatActivity() {
    private val REQUEST_BLUETOOTH_PERM = 1001
    private lateinit var bluetoothAdapter: BluetoothAdapter
    private lateinit var discoveryReceiver: BroadcastReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_bluetooth_scan)

        // 初始化蓝牙适配器
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() ?: run {
            Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show()
            finish()
            return
        }

        // 初始化广播接收器
        initDiscoveryReceiver()
        // 注册广播
        registerReceiver(discoveryReceiver, IntentFilter().apply {
            addAction(BluetoothDevice.ACTION_FOUND)
            addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
        })

        // 检查蓝牙状态
        if (!bluetoothAdapter.isEnabled) {
            startActivityForResult(Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), REQUEST_BLUETOOTH_PERM)
        } else {
            checkAndRequestPermissions()
        }
    }

    private fun initDiscoveryReceiver() {
        discoveryReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                intent ?: return
                when(intent.action) {
                    BluetoothDevice.ACTION_FOUND -> {
                        val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
                        device?.let {
                            Log.d("BluetoothScan", "设备:${it.name ?: "无名"} | ${it.address}")
                        }
                    }
                    BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                        Toast.makeText(context, "扫描完成", Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }
    }

    private fun startDiscovery() {
        if (bluetoothAdapter.isDiscovering) {
            bluetoothAdapter.cancelDiscovery()
        }
        val isStarted = bluetoothAdapter.startDiscovery()
        Log.d("BluetoothScan", "扫描启动状态:$isStarted")
        if (!isStarted) {
            Toast.makeText(this, "扫描启动失败", Toast.LENGTH_SHORT).show()
        }
    }

    private fun checkAndRequestPermissions() {
        val requiredPermissions = arrayOf(
            Manifest.permission.BLUETOOTH_SCAN,
            Manifest.permission.ACCESS_FINE_LOCATION
        )
        val needRequest = requiredPermissions.any {
            ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
        }
        if (needRequest) {
            ActivityCompat.requestPermissions(this, requiredPermissions, REQUEST_BLUETOOTH_PERM)
        } else {
            startDiscovery()
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_BLUETOOTH_PERM && resultCode == RESULT_OK) {
            checkAndRequestPermissions()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_BLUETOOTH_PERM && grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
            startDiscovery()
        } else {
            Toast.makeText(this, "权限不足,无法扫描蓝牙设备", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(discoveryReceiver)
        if (bluetoothAdapter.isDiscovering) {
            bluetoothAdapter.cancelDiscovery()
        }
    }
}

四、Android 12+ BroadcastReceiver 关键注意事项

  1. 隐式广播限制:除少数系统白名单广播外,大部分隐式广播(如ACTION_FOUND)禁止静态注册,必须动态绑定到组件生命周期
  2. 权限范围细分:BLUETOOTH_SCAN可添加android:usesPermissionFlags="neverForLocation",无需位置权限,但仅能扫描已配对设备
  3. 后台扫描限制:应用在后台时扫描频率会被系统限制,需申请ACCESS_BACKGROUND_LOCATION权限才能正常后台扫描
  4. 生命周期绑定:动态注册的广播必须在组件销毁时注销,否则会引发内存泄漏

内容的提问来源于stack exchange,提问作者Naiara Aguirre

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 07:17:03