简体   繁体   中英

Replace BLE callbacks using Kotlin Coroutines

I want to use Kotlin's coroutines to handle BLE's asynchronous callbacks. Connecting to a BLE device takes a callback object, something like:

connectToBle(Context, Boolean, GattCallback)

The result returns asynchronously, in a method onConnectionStateChanged of GattCallback object. I used suspendCoroutine<BluetoothGatt> to implement this as detailed in the documentation here .

Now onConnectionStateChanged returns a BluetoothGatt object which I have to save as a global variable and use to call other methods such as discoverServices , readCharacteristic , writeCharacteristic etc., all of which return asynchronously in different callback methods of the GattCallback object such as onServicesDiscovered , onCharacteristicRead , onCharacteristicWrite and so on.

Here's the code using 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
        }
    })
}

Methods called on the saved gatt instance:

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

If I want to write "sequential code" as below:

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

What changes do I have to make? Should I use something other than the suspendCoroutine?

Sounds like Kable might fit your needs? It provides an API similar to what you've described.

For example, you can connect, write/read to/from a characteristic, then disconnect, with the following code:

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

Disclaimer: I'm a contributor to Kable , so this is a bit of a biased self plug. ♂️

if someone still looking for the answer, it can be achieved using coroutine channels (or maybe callbackFlow as well)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM