Android中如何指定BLE ScanCallback的运行线程?
Android & Kotlin:控制BLE ScanCallback的运行线程
Android的BLE框架默认会将ScanCallback的onScanResult、onScanFailed等回调方法投递到主线程执行,这是系统API的设计规则——哪怕你在Dispatcher.IO线程里创建回调对象,系统依然会把回调事件发送到主线程Looper,和创建回调的线程无关。
如何让回调运行在与scan()方法相同的线程?
结合你已使用协程的场景,优先推荐以下方案:
方案1:利用协程调度器切换上下文
在回调方法内部,通过CoroutineScope配合类中持有的ioDispatcher,将业务逻辑切换到Dispatcher.IO线程执行,保证回调逻辑和scan()方法的其余代码运行在同一调度器的线程池中。
修改后的onScanResult示例:
override fun onScanResult(callbackType: Int, result: ScanResult?) { // 回调本身仍在主线程,但业务逻辑切换到IO调度器 CoroutineScope(ioDispatcher).launch { if (Looper.myLooper() == Looper.getMainLooper()) { Log.d(TAG, "This is running MAIN thread") } else { Log.d(TAG, "This is running NOT on MAIN thread") } try { result?.device?.let { bleDevices.put(it, SignalStrength(result.rssi)) } } catch (t: Throwable) { if (cont.isActive) { cont.resumeWithException(t) } } } }
注意:bleDevices是线程不安全的MutableMap,建议换成ConcurrentHashMap或保留@Synchronized注解,避免多线程读写冲突。
方案2:自定义Handler绑定目标线程
若不想用协程,可在scan()方法的withContext(ioDispatcher)块内,创建绑定当前线程Looper的Handler(需确保线程有Looper,可通过HandlerThread实现),再在回调中把任务post到该Handler上执行。但此方案相对繁琐,不优先推荐。
针对你的需求场景(停止扫描后延迟重启)
在onScanFailed中,同样通过协程切换到IO线程执行延迟重启逻辑即可:
override fun onScanFailed(errorCode: Int) { CoroutineScope(ioDispatcher).launch { try { when (errorCode) { SCAN_FAILED_ALREADY_STARTED -> { // 先停止扫描 bluetoothAdapter.bluetoothLeScanner.stopScan(scanCallback) delay(3000) // 等待3秒 // 重启扫描 bluetoothAdapter.bluetoothLeScanner.startScan(scanCallback) } else -> { /* 其他错误码处理逻辑 */ } } } catch (t: Throwable) { if (cont.isActive) { cont.resumeWithException(t) } } } }
内容的提问来源于stack exchange,提问作者Karolis
相关产品推荐
相关产品推荐

