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

时间:2018-09-27 10:22:35

标签: android kotlin bluetooth-lowenergy

我正在尝试在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对我来说是一个新世界!

谢谢, 亚当

2 个答案:

答案 0 :(得分:2)

我在评论中张贴了一些笔记,但我认为将其格式化为答案会更好。

即使您已经解决了问题,我还是建议异步运行实际的协程,并在其中等待使用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()
                }
            }
        }
    }
}

答案 1 :(得分:0)

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

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

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

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

runOnUiThread(subscribe)