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 ?