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

Android开发:是否存在无需回调的BLE设备扫描方法?

嘿,我来帮你解决这个BLE扫描的问题!

首先明确一点:Android原生的BLE API并没有直接同步返回扫描结果的函数——因为BLE扫描本质是异步操作,系统在后台处理设备探测,没法直接阻塞线程等待结果返回。你遇到的回调方案不好用的问题,其实可以通过封装异步逻辑来解决,让调用起来更像“同步获取列表”的感觉,同时适配你每秒多次扫描的需求。

下面给你两个实用的方案,都是基于原生API优化的:


方案1:用协程封装,把异步回调转成挂起函数(推荐)

如果你的项目用Kotlin,协程可以完美解决异步回调的痛点,把扫描逻辑封装成一个挂起函数,调用时就能直接拿到扫描结果列表,就像同步方法一样。

核心思路:

  • 用CompletableDeferred来等待扫描结束
  • 启动扫描后,在回调里收集设备结果
  • 扫描指定时长(比如1秒)后停止扫描,返回结果列表

代码示例:

import kotlinx.coroutines.*
import android.bluetooth.le.*
import android.bluetooth.BluetoothAdapter
import java.util.concurrent.CopyOnWriteArrayList

class BleScannerManager {
    private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
    private val bleScanner: BluetoothLeScanner? = bluetoothAdapter?.bluetoothLeScanner
    private var activeScanCallback: ScanCallback? = null
    // 用线程安全的列表存扫描结果,避免多线程问题
    private val scanResults = CopyOnWriteArrayList<ScanResult>()

    /**
     * 扫描BLE设备,指定扫描时长后返回结果列表
     * @param scanDurationMs 扫描时长(默认1000ms)
     * @return 扫描到的设备列表
     */
    suspend fun scanBleDevices(scanDurationMs: Long = 1000): List<ScanResult> {
        return withContext(Dispatchers.IO) {
            val resultDeferred = CompletableDeferred<List<ScanResult>>()
            
            // 清空上次的扫描结果
            scanResults.clear()

            // 创建扫描回调
            activeScanCallback = object : ScanCallback() {
                override fun onScanResult(callbackType: Int, result: ScanResult) {
                    super.onScanResult(callbackType, result)
                    // 避免重复添加同一设备(可以根据设备地址判断)
                    if (!scanResults.any { it.device.address == result.device.address }) {
                        scanResults.add(result)
                    }
                }

                override fun onScanFailed(errorCode: Int) {
                    super.onScanFailed(errorCode)
                    resultDeferred.completeExceptionally(
                        RuntimeException("BLE扫描失败,错误码:$errorCode")
                    )
                }
            }

            try {
                // 开始扫描
                bleScanner?.startScan(activeScanCallback)
                // 等待指定时长
                delay(scanDurationMs)
                // 停止扫描
                bleScanner?.stopScan(activeScanCallback)
                // 返回结果副本
                resultDeferred.complete(scanResults.toList())
            } catch (e: Exception) {
                resultDeferred.completeExceptionally(e)
            } finally {
                activeScanCallback = null
            }

            resultDeferred.await()
        }
    }

    /**
     * 主动停止扫描(比如页面销毁时调用)
     */
    fun stopScan() {
        activeScanCallback?.let { bleScanner?.stopScan(it) }
        activeScanCallback = null
        scanResults.clear()
    }
}

调用方式(在Activity/ViewModel里):

val bleScannerManager = BleScannerManager()

// 每秒扫描一次的示例
lifecycleScope.launch {
    while (isActive) {
        try {
            val devices = bleScannerManager.scanBleDevices(1000)
            // 处理扫描到的设备列表
            println("扫描到${devices.size}个BLE设备:${devices.map { it.device.name ?: it.device.address }}")
        } catch (e: Exception) {
            println("扫描出错:${e.message}")
        }
        // 这里可以根据需求调整间隔,比如扫描1秒,休息0秒就是每秒一次
        delay(0)
    }
}

// 页面销毁时记得停止扫描
override fun onDestroy() {
    super.onDestroy()
    bleScannerManager.stopScan()
}

方案2:用定时器+线程安全列表(Java/Kotlin通用)

如果不用协程,也可以手动管理扫描状态:每次启动扫描前清空结果列表,设置定时器在指定时长后停止扫描,然后读取列表。

核心步骤:

  1. 初始化一个线程安全的列表(比如CopyOnWriteArrayList)存储扫描结果
  2. 启动扫描时清空列表,注册回调添加设备
  3. 用Handler或Timer在1秒后停止扫描,然后处理列表中的结果

重要注意事项:

  • 权限问题:必须申请对应的权限:
    • API 31+:BLUETOOTH_SCAN、BLUETOOTH_ADMIN,如果不需要位置信息,要在权限里加android:usesPermissionFlags="neverForLocation"
    • API 23-30:ACCESS_FINE_LOCATION(或ACCESS_COARSE_LOCATION)、BLUETOOTH_ADMIN
  • 蓝牙状态检查:扫描前要确保蓝牙已开启,可通过bluetoothAdapter?.isEnabled判断,未开启时可以跳转到蓝牙设置页面
  • 耗电与系统限制:每秒扫描一次频率很高,会增加设备耗电,部分Android系统可能会限制频繁扫描的行为,建议根据实际需求调整扫描间隔
  • 内存泄漏:一定要在不需要扫描时(比如页面销毁)停止扫描,释放回调引用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:14:55