Kotlin 回调未在异步函数中调用



我正在尝试订阅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)
}
}
}
}
}
}

在一篇相关帖子中有人建议我在主线程中运行gatt.writeDescriptor()函数。正如您在上面的代码中看到的那样,我已经尝试过使用runOnUiThread()并按照此问题的建议创建一个 Handler 对象,但无济于事。

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

编辑:看起来while(!canContinue);循环实际上阻止了回调。如果我注释掉这一行,回调会触发,但随后我会遇到与以前相同的问题。如何阻止此功能?

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

谢谢 亚当

我在评论中发布了一些注释,但我认为最好将其格式化为答案。

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

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

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

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

只需从主线程中调用gatt.writeDescriptor()函数和while循环即可解决此问题:

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

最新更新