【问题标题】:Replace BLE callbacks using Kotlin Coroutines使用 Kotlin 协程替换 BLE 回调
【发布时间】:2020-07-28 19:19:07
【问题描述】:

我想使用 Kotlin 的协程来处理 BLE 的异步回调。连接到 BLE 设备需要一个回调对象,例如:

connectToBle(Context, Boolean, GattCallback)

结果在 GattCallback 对象的 onConnectionStateChanged 方法中异步返回。我使用suspendCoroutine<BluetoothGatt> 来实现这一点,详见文档here

现在onConnectionStateChanged 返回一个BluetoothGatt 对象,我必须将其保存为全局变量并用于调用其他方法,例如discoverServicesreadCharacteristicwriteCharacteristic 等,所有这些方法都在不同的回调中异步返回GattCallback 对象的方法如onServicesDiscoveredonCharacteristicReadonCharacteristicWrite 等。

这是使用suspendCoroutine的代码:

suspend fun BluetoothDevice.connectToBleDevice(
    context: Context,
    autoConnect: Boolean = false
) = suspendCoroutine<BluetoothGatt?> { cont ->

    connectGatt(context, autoConnect, object : BluetoothGattCallback() {

        override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
            super.onConnectionStateChange(gatt, status, newState)
            Timber.d("onConnectionStateChange: ")
            if (status != BluetoothGatt.GATT_SUCCESS) cont.resume(null) else cont.resume(gatt)
            // save gatt instance here if success
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
            super.onServicesDiscovered(gatt, status)
            if (status != BluetoothGatt.GATT_SUCCESS) cont.resume(null) else cont.resume(gatt)
            // return list of services if success
        }

        override fun onCharacteristicRead(
            gatt: BluetoothGatt?,
            characteristic: BluetoothGattCharacteristic?,
            status: Int
        ) {
            super.onCharacteristicRead(gatt, characteristic, status)
            if (status != BluetoothGatt.GATT_SUCCESS) cont.resume(null) else cont.resume(gatt)
            // return read value if success
        }
    })
}

在保存的 gatt 实例上调用的方法:

    fun discoverServices() {
        gatt?.discoverServices() // result received in onServicesDiscovered
    }

    fun readCharacteristic(serviceUUID: UUID, characteristicUUID: UUID) {
        gatt?.apply {
            val characteristic =
                getService(serviceUUID).getCharacteristic(characteristicUUID)
            readCharacteristic(characteristic) // result received in onCharacteristicRead
        }
    }

如果我想写“顺序代码”如下:

val gatt = connectToBle(context, false, gattCallback) // suspend until onConnectionStateChanged returns successfully
discoverServices()                                    // suspend until discoverServices returns successfully
writeCharacteristic(characteristic, valueToWrite)     // suspend until value is written successfully
val valueRead = readCharacteristic(characteristic)    // suspend until the value is read successfully
disconnect()

我必须做出哪些改变?我应该使用suspendCoroutine以外的东西吗?

【问题讨论】:

  • 也许我在这里遗漏了这个问题,但您遇到的问题到底是什么?
  • @tyczj 我希望 GATT 方法暂停,直到相应的回调返回,但我没有看到这种情况发生。 connect 方法按预期挂起,我得到了 GATT 对象,但其余方法立即返回。
  • 在“顺序代码”块下进行了编辑。 GATT 实例将保存在与suspendCoroutine 相同的类中,并且在此实例本身上调用所有其他方法。 onConnectionStateChange 是为connectToBle 返回的回调,并且在它返回后立即继续继续。如果我希望所有 GATT 方法都“暂停”,即在相应的回调返回后返回,我应该怎么做?
  • 由于 API 的构建方式,我认为您无法实现这一点,因为 BluetoothGattCallback 有多个回调

标签: android kotlin bluetooth-lowenergy kotlin-coroutines


【解决方案1】:

听起来像Kable 可能符合您的需求?它提供了一个类似于您所描述的 API。

例如,您可以使用以下代码连接、写入/读取/读取特性,然后断开连接:

val characteristic = characteristicOf(
    service = "00001815-0000-1000-8000-00805f9b34fb",
    characteristic = "00002a56-0000-1000-8000-00805f9b34fb"
)

// todo: Use scanner (provided by Kable) to obtain `peripheral`.

peripheral.apply {
    connect()
    write(characteristic, byteArrayOf(1, 2, 3))
    val valueRead: ByteArray = read(characteristic)
    disconnect()
}

免责声明:我是Kable 的贡献者,所以这有点偏颇。 ?‍♂️

【讨论】:

    猜你喜欢
    • 2019-12-06
    • 1970-01-01
    • 2020-01-10
    • 2021-02-01
    • 2021-09-25
    • 2021-05-13
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    相关资源
    最近更新 更多