Android + BLE + Kotlin + RxJava3
Bluetooth Low Energy on Android made easy with RxJava.
Made with love at the Equisense HQ. This library is used in our Equisense app since years.
Looking for BLE with Coroutines instead of RxJava ? Take a look at the LouisCAD's implementation.
Starting from Android API 31, the fine location permission is not required anymore, instead of this, you have to use the BLUETOOTH_CONNECT and BLUETOOTH_SCAN permissions when dealing the bluetooth low energy framework. Before upgrading targetSdkVersion
to 31 in your app, check your requestPermission
calls according to this new permission.
Because of this change, RxBluetoothKotlin was updated to fire the NeedBluetoothScanPermission
and NeedBluetoothConnectPermission
exceptions at the right moment if they're missing at the runtime. Theses exceptions are fired since the release 3.2.0
.
RxBluetoothKotlin is released on Maven Central since the version 3.1.0
you don't have to worry about this library when jCenter will shutdown ! Unfortunately, according to the Maven Central policies, I must update my package to match with the host domain I own. I only own masselis.com
, so the package name RxBluetothKotlin were renamed from com.vincentmasselis.rxbluetoothkotlin
to com.masselis.rxbluetoothkotlin
, as consequence, you have to renamed EVERY import from rxbluetoothkotlin to the new package name.
// Check the github release section to find the latest available version
implementation 'com.masselis.rxbluetoothkotlin:rxbluetoothkotlin-core:<<latest_version>>'
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.rxScan()
.subscribe { scanResult ->
}
bluetoothDevice.connectRxGatt()
.flatMapMaybe { gatt -> gatt.whenConnectionIsReady().map { gatt } }
.subsribe { rxBluetoothGatt ->
}
rxBluetoothGatt.read(characteristic)
.subscribe { byteArray ->
}
rxBluetoothGatt.write(characteristic, byteArray)
.subscribe { _ ->
}
rxBluetoothGatt.enableNotification(characteristic)
.flatMapPublisher { listenChanges(characteristic) }
.subscribe { byteArray ->
}
rxBluetoothGatt.disconnect().subscribe()
- Min target API 18
- When scanning with RxBluetoothKotlin, you have to grant theses runtime permissions:
- From Android 6 to 9 inclusive:
ACCESS_COARSE_LOCATION
- From Android 10 to 11 inclusive:
ACCESS_FINE_LOCATION
- From Android 12:
BLUETOOTH_SCAN
- From Android 6 to 9 inclusive:
- When connecting with RxBluetoothKotlin, you have to grant this runtime permission:
- From Android 12:
BLUETOOTH_CONNECT
- From Android 12:
- A turned on bluetooth chip
(context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter.isEnabled
- You can add to your manifest
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
RxBluetoothKotlin declare the BLUETOOTH_SCAN
permission into the AndroidManifest with the property usesPermissionFlags="neverForLocation"
. If you want to remove the usesPermissionFlags
property, you have to add tools:remove="usesPermissionFlags"
to your uses-permission
node into your own AndroidManifest, if you want to update usesPermissionFlags
by setting an other value you have to use tools:replace="usesPermissionFlags"
instead.
When scanning with rxScan()
or connecting with connectRxGatt()
, you can set the logger
parameter. By setting it, RxBluetoothKotlin will produce a log for every bluetooth input, output, starting scan, error thrown, etc.. I recommend to set it for debugging purposes and/or if you're not familiar with the Android BLE API. It could helps you a lot to understand what's going on between your app and the Bluetooth Low Energy device.
Interact with Bluetooth Low Energy devices on Android is hard. The BLE specs uses unfamiliar concepts, the BLE API from Android could fails at any moment and some exceptions are silent. Because of this, a basic method like write(BluetoothGattCharacteristic)
could fails for 5 different reasons. It becomes harder if you are chaining this call with other calls, this sum up to a thrown exception when it's impossible to known which call fails and why.
For this reason, every public method from this library is documented with every exceptions which could be fired and most of the exception are unique. For example: write(BluetoothGattCharacteristic)
could fire:
- Unique
CharacteristicWriteDeviceDisconnected
if the device disconnects while writing - Unique
CannotInitializeCharacteristicWrite
if the Android API returnedfalse
when callingwriteCharacteristic
- Unique
CharacteristicWriteFailed
if the characteristic could not be written BluetoothIsTurnedOff
if bluetooth...... is turned off 🤷🏻♀️BluetoothTimeout
if writing takes more than 1 minute
All the 3 uniques exceptions are containing the required data to understand why it failed. For example CharacteristicWriteDeviceDisconnected
has 5 fields, bluetoothDevice
, status
, service
, characteristic
and value
, all of theses fields should helps you to understand what's going on.
If you're not familiar with the Bluetooth Low Energy API or if you want to try this lib, you can build and run the demo app from this repo.
⚠ Before reading this part, you must know how a Decorator design pattern works and how to make a new one.
On Android, communicating with a Bluetooth device requires an instance of BluetoothGatt
and an instance of BluetoothGattCallback
. RxBluetoothKotlin wraps both of theses types into RxBluetoothGatt
and RxBluetoothGatt.Callback
types to add some reactive touch to the system objects. Both RxBluetoothGatt
and RxBluetoothGatt.Callback
are interfaces, calling connectRxGatt
will return a default implementation for both of them but you are free to wrap the returned implementation by your own implementation to add you own behavior, you only have to follow the Decorator rules.
The following diagram will show you which classes are used to create the decorator pattern:
As you can see, to create a decorator, you only have to subclass SimpleRxBluetoothGatt
. If you want to decorate RxBluetoothGatt.Callback
just subclass SimpleRxBluetoothGattCallback
like you do with SimpleRxBluetoothGatt
from the previous example.
When your decorators are written you can send them to RxBluetoothKotlin by setting the rxGattBuilder
and rxCallbackBuilder
parameters when calling connectRxGatt
. Defaults implementation of RxBluetoothKotlin uses RxBluetoothGattImpl
and RxBluetoothGattCallbackImpl
, by using you own decorator you can change the way RxBluetoothKotlin is communicating with the Android SDK in order to match your own requirements.
Report an issue by using github
Follow me on Twitter @VincentMsls
Discover our Equisense sensors
//TODO
- Getting started