KB: Simplifying Android Bluetooth Low Energy development with Coroutines, Flows and Jetpack Compose

Prerequisite

The source code of the RSL15 Central Android app is not publicly available. Please contact your local onsemi sales representative to request a copy of the RSL15 Central source code.

Introduction

Even though Android has evolved since it’s official public release in 2008 working with Bluetooth Low Energy on Android can be a nightmare especially on the earlier version of Android. The lack of documentation about the various error codes that the Bluetooth Low Energy stack throws, and the synchronization issues that developers need to handle while performing Bluetooth Low Energy operations, are some of the reasons why it is difficult to develop Android Bluetooth Low Energy applications.

The RSL15 Central application can help developers to quickly get started with Android Bluetooth Low Energy development, using the latest technologies that Android has to offer such as coroutines, flows and jetpack compose. RSL15 Central is a simple application that establishes a Bluetooth Low Energy connection to the RSL15 Evaluation and Development Board and demonstrates two-way data transmission between a phone and the RSL15 device. The application displays measured battery capacity in real-time. The Green LED on the RSL15 EVB can be toggled by the LED switch in the application, and the push button state (SW1) on the RSL15 EVB is displayed in the RSL15 Central application.

The sections below cover how RSL15 Central can simplify Android Bluetooth Low Energy development with coroutines and flows. In addition, it helps you to learn about the benefits of using Jetpack Compose for developing the application UI.

RSL15 Central Application Architecture

The RSL15 Central Android application is designed using the MVVM design pattern.

The MainActivity.kt class contains the NavGraph, which handles the Navigation component and keeps track of the back stack of the composables that make up the screens in the RSL15 Central app. The ViewModels in the app are responsible for preparing and managing the state of the UI for composables. The BluetoothCentral.kt class contains the implementation, showing how to connect to the device, discover services, read, and write characteristics. This class is dependent on BluetoothGattCallback.kt, which implements the BluetoothGatt callbacks such as onServiceDiscovered, onCharactertisticRead, onCharactertisiticWrite, etc. and uses SharedFlow to emit the values.

BLE API calls for the RSL15 Central app

In the RSL15 Central app, getDescriptor is used to get the Client Characteristic Configuration Descriptor (CCCD) for the battery and button characteristics. The CCCD value is a two-bit bitfield, with one bit corresponding to notifications and the other to indications.

Services supported by RSL15 Central

The RSL15 Central app requires the device firmware to support the Bluetooth Low Energy “Battery” and onsemi “LED and Button” custom services to function properly.

uuid

Coroutines

Coroutines are like light-weight threads that can run in parallel and wait for each other and communicate. The main difference between a thread and coroutine is that you pay very little in terms of performance with a coroutine because switching between coroutines need not involve any system calls or any blocking calls.

Callback with coroutines

Although callbacks are a common solution for asynchronous communication, they come with some drawbacks, such as incomprehensible code and difficulty in error handling. In Kotlin, you can simplify calling callbacks by using coroutines.

The callbackFlow builder can be used to convert callback-based APIs, such as ScanCallback to flow. To convert the callback-based APIs to flow, you normally need to follow these three basic steps:

  1. Implement the callback interface and use trySend to add elements to the flow.

  2. Register the callback.

  3. Suspend until the flow collector is closed and unregister the callback.

callbackFlow

Bluetooth Low Energy operation using SharedFlow

In Android, performing multiple Bluetooth Low Energy operations in quick succession produces unexpected results. For example, if you want to read from/write to multiple characteristics back-to-back, you can see that only the first operation succeeds, and other operations fail. One way to resolve this issue is by implementing a queue for Bluetooth Low Energy operations. Although this solution is good enough it can be difficult to implement. Thus, Kotlin provides a better way to perform Bluetooth Low Energy operation by using SharedFlow.

SharedFlow is a hot Flow that shares emitted values among all its collectors in a broadcast fashion, so that all collectors receive all emitted values. It is a hot Flow because it exists regardless of whether it is being collected or not. For example, the following class encapsulates an event bus that distributes events to all subscribers. The extraBufferCapacity is to provide a cushion for fast Emitters and slow Collectors.

sharedFlow

Jetpack Compose

Jetpack Compose is a modern toolkit for building native Android UI. It simplifies and accelerates UI development by enabling developers to use one language for everything, including the UI (i.e. Kotlin). Developers no longer need to use XML for layout, color, style, and typography; instead, they can write composable functions to define the UI elements. Since Compose follows the declarative approach, it is easy to define the UI using the Kotlin language features.

In addition, as the name suggests, Compose makes use of composition as opposed to the traditional Android UI, which makes use of inheritance.
Jetpack Compose

1 Like