RxAndroidBLE — your most powerful tool for Bluetooth Low Energy coding!

​Some time ago we published here, a tutorial on how to work with Bluetooth Low Energy in Android. As you might have noticed, the whole process required many (many!) asynchronous callbacks, handling different numeric statuses, threading knowledge and more. Because at Polidea, our customer’s products become more and more intelligent and connected it was natural for us to simplify all this code duplication and complexity.

​So we did it, and here it is! Let us introduce you to our new powerful toy, the RxAndroidBLE library. ​ RxAndroidBle is the painkiller for Android’s Bluetooth Low Energy headaches. It is backed with RxJava, it basically implements complicated APIs as handy reactive observables. Long story short, the library can: ​

  • Fancy asynchronous operations support (read, write, notifications)
  • Thread management in order to meet Android contracts
  • Connection and operation error handling, etc.

In the following post, we will give you tips to get started with RxAndroidBLE library. ​ ​

Including into the project

The library is available for your convenience as Maven dependency on Central. Depending on your build system choice you use either: ​

Maven

<dependency> <groupId>com.polidea.rxandroidble</groupId> <artifactId>rxandroidble</artifactId> <version>1.0.1</version> <type>aar</type> </dependency>

Gradle

compile "com.polidea.rxandroidble:rxandroidble:1.0.1"

Obtaining the client

Your entry point to the library is the RxBleClient class. For connection and operation handling we require that single instance of the client is used per app. It’s a good idea to use some dependency injection like Dagger and it’s scoped components but it’s up to you. Simple singletons and Application based containers are alright to use as well. The client itself can be obtained by simply call to: ​

RxBleClient rxBleClient = RxBleClient.create(context)

Finding a device

Device discovery is very simple and handy. Scan lifecycle management is linked to the Observable lifecycle. It is safe to subscribe more subscribers to the same observable so you don’t have to worry if there are any scans already taking place, as long as they are initiated through the library. There are some future plans for more advanced filtering. ​ By default there is no filtering but you can pass single or many service UUIDs through the var-arg. You can also safely use your own 128-bit UUIDs as we handle the Android’s bug for you. ​

Subscription scanSubscription = rxBleClient.scanBleDevices() .subscribe(rxBleScanResult -> { // Process scan result here. }); // When done, just unsubscribe. scanSubscription.unsubscribe();

​ Note: It’s a good idea to chain it with the RxLifecycle. ​

Connecting to the device

In order to connect you will need the RxBleDevice instance. It can be taken from the scan result or created manually with a mac address of the Bluetooth device. All you need to do is to call establishConnection method and get the RxBleConnection, which is a handle, used to process BLE operations with a connected device. ​

String macAddress = "AA:BB:CC:DD:EE:FF"; RxBleDevice rxBleDevice = rxBleClient.getBleDevice(macAddress); ​ Subscription subscription = rxBleDevice.establishConnection(context, false) .subscribe(rxBleConnection -> { }); subscription.unsubscribe();

​ By default there can be only one connection call at time. Head to examples and the ConnectionSharingAdapter to learn how to share it. ​

Discovering services

Although it is not require to do it manually in order to read/write, you can always discover supported services with the following call. ​

rxBleDevice.establishConnection(this, false) .flatMap(RxBleConnection::discoverServices) .subscribe(RxBleDeviceServices discoveryResult -> { // Process service discovery result on your own. }); ​

Reading / writing to the device

Almost no BLE device is useful if it cannot be interacted. Once you are connected you can operate as presented below: ​

Read

device.establishConnection(context, false) .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID)) .subscribe(characteristicValue -> { }); ​

Write

device.establishConnection(context, false) .flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, bytesToWrite)) .subscribe(characteristicValue -> { });

Multiple reads

device.establishConnection(context, false) .flatMap(rxBleConnection -> Observable.combineLatest( rxBleConnection.readCharacteristic(firstUUID), rxBleConnection.readCharacteristic(secondUUID), YourModelCombiningTwoValues::new )) .subscribe(model -> { });

Read and write combined

device.establishConnection(context, false) .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid) .doOnNext(bytes -> { }) .flatMap(bytes -> rxBleConnection.writeCharacteristic(characteristicUuid, bytesToWrite)) .subscribe(writeBytes -> { });

You can also listen to change notifications

device.establishConnection(context, false) .flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristicUuid)) .doOnNext(notificationObservable -> { }) .flatMap(notificationObservable -> notificationObservable) .subscribe(bytes -> { }); ​

Closing connection

Do not worry about complex connection teardown. Disconnect, closing the GATT is managed for you. The only thing you need to do is to unsubscribe. As simple as that! ​

Conclusion

IoT is no longer a future but it is all over the place and Bluetooth programming became our reality. Fortunately thanks to reactive concepts it does not have to be a headache anymore. We believe that open source is a part of our mission so here we are, sharing our battlefield tested knowledge to you. For more great tutorials follow our blog and subscribe to our newsletter. Do not forget to check the repository on Github as new features are coming!


Originally published at www.polidea.com.

Show your support

Clapping shows how much you appreciated Polidea’s story.