Обратный вызов Kotlin не вызывается в асинхронной функции - PullRequest
0 голосов
/ 27 сентября 2018

Я пытаюсь подписаться на несколько характеристик периферийного устройства BLE в Android API 28.

Из-за асинхронного характера API BLE мне нужно создать функцию, которая подписывается на каждую характеристику (gatt.writeDescriptor()) блок;в противном случае API BLE будет пытаться подписаться сразу на несколько характеристик, несмотря на тот факт, что за один раз может быть записан только один дескриптор: это означает, что только одна характеристика когда-либо подписана.

Блокировка достигается путем переопределения обратного вызова 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(), и создав объект-обработчик, следуя подсказкам из этого вопроса.

Обратный вызов вызывается, если я вызываюgatt.writeDescriptor() из синхронной функции, я понятия не имею, почему она не вызывается из асинхронной функции.

EDIT: Похоже, что цикл while(!canContinue); фактически блокирует обратный вызов.Если я закомментирую эту строку, обратный вызов сработает, но тогда я столкнусь с той же проблемой, что и раньше.Как я могу заблокировать эту функцию?

Любые предложения приветствуются!Простите мое невежество, но я очень привык работать со встроенными системами, Android для меня - это новый мир!

Спасибо, Адам

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

Я написал несколько комментариев в комментариях, но решил, что лучше отформатировать их в качестве ответа.

Даже если вы уже исправили свою проблему, я бы предложил асинхронно запускать фактическую сопрограмму и внутри нее ждать уведомления о записи, используя каналы

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()
                }
            }
        }
    }
}
0 голосов
/ 27 сентября 2018

Так что я сам разобрался с ответом.

Цикл while(!canContinue); фактически блокировал обратный вызов, поскольку он выполнялся в основном потоке, и имел приоритет над обратным вызовом, необходимым для установки переменной canContinue.

Это было решено простым вызовом функции gatt.writeDescriptor() и цикла while из основного потока:

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

runOnUiThread(subscribe)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...