【问题标题】:Kotlin callback not called within async function未在异步函数中调用 Kotlin 回调
【发布时间】:2018-10-03 21:56:19
【问题描述】:

我正在尝试在 Android API 28 中订阅 BLE 外围设备的多个特性。

由于 BLE API 的异步特性,我需要制作订阅每个特征 (gatt.writeDescriptor()) 块的函数;否则 BLE API 将尝试一次订阅多个特征,尽管一次只能写入一个描述符:这意味着只能订阅一个特征。

阻塞是通过重写onServicesDiscovered回调并调用异步函数来循环和订阅特征来实现的。这被一个简单的布尔值 (canContinue) 阻止。 很遗憾,回调函数onDescriptorWrite从未被调用过。

请看下面的代码:

override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
    canContinue = true 
} 

override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { 
    runBlocking {
        loopAsync(gatt)
    }
}

private suspend fun loopAsync(gatt: BluetoothGatt) {
    coroutineScope {
        async {
            gatt.services.forEach { gattService ->                      
                gattService.characteristics.forEach { gattChar ->
                    CHAR_LIST.forEach {
                        if (gattChar.uuid.toString().contains(it)) {
                            canContinue = false
                            gatt.setCharacteristicNotification(gattChar, true)

                            val descriptor = gattChar.getDescriptor(UUID.fromString(BleNamesResolver.CLIENT_CHARACTERISTIC_CONFIG))                                     
                            descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE

                            val write = Runnable {
                                gatt.writeDescriptor(descriptor)
                            }
                            //private val mainHandler = Handler(Looper.getMainLooper())
                            //mainHandler.post(write)
                            //runOnUiThread(write)
                            gatt.writeDescriptor(descriptor)

                        }

                        while (!canContinue)
                    }
                }
            }
        }
    }
}

related post 中建议我在主线程中运行 gatt.writeDescriptor() 函数。正如您在上面的代码中看到的那样,我尝试使用 runOnUiThread() 并根据 this 问题的建议创建一个 Handler 对象均无济于事。

如果我从同步函数调用gatt.writeDescriptor(),回调会被调用,我不知道为什么它不会从异步函数调用。

编辑:看来while(!canContinue); 循环实际上阻塞了回调。如果我将此行注释掉,回调就会触发,但我会面临与以前相同的问题。怎么屏蔽这个功能?

欢迎提出任何建议!原谅我的无知,但我已经非常习惯于在嵌入式系统上工作,Android 对我来说是一个非常新的世界!

谢谢, 亚当

【问题讨论】:

  • 您的 while 循环不应该在 if 块内吗?这意味着您只有在编写描述符时才等待。
  • @ErnestZamelczyk 你说得对,虽然这对问题本身没有影响,但移动它有助于缓解未来的问题 - 谢谢

标签: android kotlin bluetooth-lowenergy


【解决方案1】:

我在 cmets 中发布了一些注释,但我认为将其格式化为答案会更好。

即使您已经解决了问题,我还是建议您异步运行实际的协程并在其中等待使用 channels 的写入通知

private var channel: Channel<Boolean> = Channel()

override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
    GlobalScope.async {
        channel.send(true)
    }
} 

override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { 
    GlobalScope.async {
        loopAsync(gatt)
    }
}

private suspend fun loopAsync(gatt: BluetoothGatt) {
    gatt.services.forEach { gattService ->                      
        gattService.characteristics.forEach { gattChar ->
            CHAR_LIST.forEach {
                if (gattChar.uuid.toString().contains(it)) {
                    gatt.setCharacteristicNotification(gattChar, true)

                    val descriptor = gattChar.getDescriptor(UUID.fromString(BleNamesResolver.CLIENT_CHARACTERISTIC_CONFIG))                                     
                    descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE

                    gatt.writeDescriptor(descriptor)
                    channel.receive()
                }
            }
        }
    }
}

【讨论】:

  • channel.send() 需要从 coroutine 或另一个 suspend 函数调用。你将如何解决这个问题?使用runBlocking的最佳方式是什么?
【解决方案2】:

所以我实际上自己想出了答案。

while(!canContinue); 循环实际上阻塞了回调,因为它在主线程中运行,并优先于设置 canContinue 变量所需的回调。

这可以通过在主线程中调用gatt.writeDescriptor() 函数和while 循环来解决:

val subscribe = Runnable {
    gatt.writeDescriptor(descriptor)
    while (!canContinue);
    }

runOnUiThread(subscribe)

【讨论】:

  • 顺便说一句,更好的方法是在后台线程上运行协程并使用channels 接收通知。所以你会用channel.receive()替换while(!canContinue)
  • 要在后台线程上运行协程,您使用GlobalScope.async 而不是runBlocking。您可以从挂起函数中删除 coroutineScope 和 async
猜你喜欢
  • 1970-01-01
  • 2015-09-19
  • 2015-01-22
  • 2017-11-18
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 2017-11-27
  • 2020-12-14
相关资源
最近更新 更多