在 Android 中,如何指定 BLE ScanCallback 在哪个线程上运行?

In Android, how to specify on which thread the BLE ScanCallback is running?

提问人:Karolis 提问时间:11/17/2023 更新时间:11/18/2023 访问量:26

问:

人造人。科特林。

您好,我希望能帮助理解线程在这里的工作原理:)

我有一个 BLE 扫描仪类。它的方法使用 运行所有内容。我设法确认它的代码不在主线程上运行。在它里面我创造了一个,在里面我创造了一个.我假设回调的方法将在创建回调的同一线程上运行。但事实并非如此 - 它在主线程上运行,可能会阻塞 UI。scan()Dispatcher.IOsuspendCancellableCoroutineScanCallback

问题:有没有办法确保 s 方法与其余方法运行在同一线程上?ScanCallback'onScanResultonScanFailedFakeScaner.scan()

原因:在某些情况下,我需要停止扫描仪,休眠几秒钟,然后重新启动它。我设法做了一个解决方法,但我仍然想弄清楚为什么它有效,以及如何在可能的情况下更改它:)

class FakeScanner(
    private val context: Context,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO) {

    private val bluetoothAdapter: BluetoothAdapter by lazy {
        val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        requireNotNull(bluetoothManager.adapter)
    }

    suspend fun scan(timeout: Duration?): Map<MyDevice, SignalStrength> =
        withContext(ioDispatcher) {
            if (Looper.myLooper() == Looper.getMainLooper()) {
                Log.d(TAG, "This is running MAIN thread")
            } else {
                Log.d(TAG, "This is running NOT on MAIN thread")
            }

            /**
             * Some code here
             */

            suspendCancellableCoroutine { cont ->

                val bleDevices = mutableMapOf<BluetoothDevice, SignalStrength>()

                val scanCallback = object : ScanCallback() {

                    @Synchronized
                    override fun onScanResult(callbackType: Int, result: ScanResult?) {

                        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) }
                        }
                    }

                    @Synchronized
                    override fun onScanFailed(errorCode: Int) {
                        try {
                            // Some logic about scan attempt limits..
                            when (errorCode) {
                                SCAN_FAILED_ALREADY_STARTED -> { /* Restart scanner after delay */ }
                                else -> { /* Logic about other error codes */ }
                            }
                        } catch (t: Throwable) {
                            if (cont.isActive) { cont.resumeWithException(t) }
                        }
                    }
                }

                try {
                    // Start scanning
                } catch (t: Throwable) {
                    if (cont.isActive) {
                        cont.resumeWithException(t)
                    }
                }

                cont.invokeOnCancellation {
                    try {
                        // Stop scanner here
                    } catch (t: SecurityException) {
                        // Nothing to do here
                    }
                }
            }
        }
}
Kotlin 低功耗 Android-蓝牙

评论


答:

2赞 Emil 11/18/2023 #1

不,不幸的是没有。 这里是硬编码的,它将在主线程上运行:https://android.googlesource.com/platform/packages/modules/Bluetooth/+/aa58f747f37494c619a9afae1622f56001bc07fb/framework/java/android/bluetooth/le/BluetoothLeScanner.java#543

之前,在 Android 4.4 中,扫描结果直接在 Binder 线程上传送,该线程在内部接收来自蓝牙进程的结果。我真的不知道他们为什么这样改变它。

如果要在其他线程上处理结果,则需要自行手动将结果发布到该线程。

评论

0赞 Karolis 11/18/2023
谢谢!这很不幸,但至少现在我知道了:)