RxAndroidBle is a powerful painkiller for Android's Bluetooth Low Energy headaches. It is backed by RxJava, implementing complicated APIs as handy reactive observables. The library does for you:
- Fancy asynchronous operations support (read, write, notifications)
- Threading management in order to meet Android contracts
- Connection and operation error handling
For support head to StackOverflow #rxandroidble
Read the official announcement at Polidea Blog.
It's your job to maintain single instance of the client. You can use singleton, scoped Dagger component or whatever else you want.
RxBleClient rxBleClient = RxBleClient.create(context);
Scanning devices in the area is simple as that:
Subscription scanSubscription = rxBleClient.scanBleDevices()
.subscribe(
rxBleScanResult -> {
// Process scan result here.
},
throwable -> {
// Handle an error here.
}
);
// When done, just unsubscribe.
scanSubscription.unsubscribe();
For further BLE interactions the connection is required.
String macAddress = "AA:BB:CC:DD:EE:FF";
RxBleDevice device = rxBleClient.getBleDevice(macAddress);
Subscription subscription = device.establishConnection(false) // <-- autoConnect flag
.subscribe(
rxBleConnection -> {
// All GATT operations are done through the rxBleConnection.
},
throwable -> {
// Handle an error here.
}
);
// When done... unsubscribe and forget about connection teardown :)
subscription.unsubscribe();
After https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback): autoConnect boolean: Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true).
Auto connect concept may be misleading at first glance. With the autoconnect flag set to false the connection will end up with an error if a BLE device is not advertising when the RxBleDevice#establishConnection
method is called. From platform to platform timeout after which the error is emitted differs, but in general it is rather tens of seconds than single seconds.
Setting the auto connect flag to true allows you to wait until the BLE device becomes discoverable. The RxBleConnection
instance won't be emitted until the connection is fully set up. From experience it also handles acquiring wake locks, so it's safe to assume that your Android device will be woken up after the connection has been established - but it is not a documented feature and may change in the future system releases.
Be careful not to overuse the autoConnect flag. On the other side it has negative impact on the connection initialization speed. Scanning window and interval is lowered as it is optimized for background use and depending on Bluetooth parameters it may (and usually do) take more time to establish the connection.
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
.subscribe(
characteristicValue -> {
// Read characteristic value.
},
throwable -> {
// Handle an error here.
}
);
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, bytesToWrite))
.subscribe(
characteristicValue -> {
// Characteristic value confirmed.
},
throwable -> {
// Handle an error here.
}
);
device.establishConnection(false)
.flatMap(rxBleConnection -> Observable.combineLatest(
rxBleConnection.readCharacteristic(firstUUID),
rxBleConnection.readCharacteristic(secondUUID),
YourModelCombiningTwoValues::new
))
.subscribe(
model -> {
// Process your model.
},
throwable -> {
// Handle an error here.
}
);
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.createNewLongWriteBuilder()
.setCharacteristicUuid(uuid) // required or the .setCharacteristic()
// .setCharacteristic() alternative if you have a specific BluetoothGattCharacteristic
.setBytes(byteArray)
// .setMaxBatchSize(maxBatchSize) // optional -> default 20 or current MTU
// .setWriteOperationAckStrategy(ackStrategy) // optional to postpone writing next batch
.build()
)
.subscribe(
byteArray -> {
// Written data.
},
throwable -> {
// Handle an error here.
}
);
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid)
.doOnNext(bytes -> {
// Process read data.
})
.flatMap(bytes -> rxBleConnection.writeCharacteristic(characteristicUuid, bytesToWrite))
)
.subscribe(
writeBytes -> {
// Written data.
},
throwable -> {
// Handle an error here.
}
);
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristicUuid))
.doOnNext(notificationObservable -> {
// Notification has been set up
})
.flatMap(notificationObservable -> notificationObservable) // <-- Notification has been set up, now observe value changes.
.subscribe(
bytes -> {
// Given characteristic has been changes, here is the value.
},
throwable -> {
// Handle an error here.
}
);
If you want to observe changes in device connection state just subscribe like below. On subscription you will receive the most current state instantly.
device.observeConnectionStateChanges()
.subscribe(
connectionState -> {
// Process your way.
},
throwable -> {
// Handle an error here.
}
);
For connection debugging you can use extended logging
RxBleClient.setLogLevel(RxBleLog.DEBUG);
Every error you may encounter is provided via onError callback. Each public method has JavaDoc explaining possible errors.
From different interfaces, you can obtain different Observable
s which exhibit different behaviours.
There are three types of Observable
s that you may encounter.
- Single value, completing — i.e.
RxBleConnection.readCharacteristic()
,RxBleConnection.writeCharacteristic()
, etc. - Multiple values, not completing - i.e.
RxBleClient.scan()
,RxBleDevice.observeConnectionStateChanges()
andObservable
emitted byRxBleConnection.setupNotification()
/RxBleConnection.setupIndication()
- Single value, not completing — these usually are meant for auto cleanup upon unsubscribing i.e.
setupNotification()
/setupIndication()
— when you will unsubscribe the notification / indication will be disabled
RxBleDevice.establishConnection()
is an Observable
that will emit a single RxBleConnection
but will not complete as the connection may be later a subject to an error (i.e. external disconnection). Whenever you are no longer interested in keeping the connection open you should unsubscribe from it which will cause disconnection and cleanup of resources.
The below table contains an overview of used Observable
patterns
Interface | Function | Number of values | Completes |
---|---|---|---|
RxBleClient | scanBleDevices()* | Infinite | false |
RxBleDevice | observeConnectionStateChanges() | Infinite | false |
RxBleDevice | establishConnection()* | Single | false |
RxBleConnection | discoverServices() | Single | true |
RxBleConnection | setupNotification()* | Single | false |
RxBleConnection | setupNotification() emitted Observable | Infinite | false |
RxBleConnection | setupIndication()* | Single | false |
RxBleConnection | setupIndication() emitted Observable | Infinite | false |
RxBleConnection | getCharacteristic() | Single | true |
RxBleConnection | readCharacteristic() | Single | true |
RxBleConnection | writeCharacteristic() | Single | true |
RxBleConnection | readDescriptor() | Single | true |
RxBleConnection | writeDescriptor() | Single | true |
RxBleConnection | readRssi() | Single | true |
RxBleConnection | requestMtu() | Single | true |
RxBleConnection | queue() | User defined | User defined |
LongWriteOperationBuilder | build() | Single | true |
* this Observable
when unsubscribed closes/cleanups internal resources (i.e. finishes scan, closes a connection, disables notifications)
We encourage you to check the package com.polidea.rxandroidble.helpers
which contains handy reactive wrappers for some typical use-cases.
If you would like to observe BluetoothAdapter
state changes you can use RxBleAdapterStateObservable
.
RxAndroidBle already provides all the necessary bluetooth permissions for you. Recently, Google has started checking these when releasing to the Play Store. If you have ACCESS_COARSE_LOCATION set manually you may run into an issue where your permission do not merge with RxAndroidBle's, resulting in a failure to upload to the Play Store This permission is only required on SDK 23+. If you need this permission on a lower version of Android use:
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="22"/>
Complete usage examples are located in /sample
GitHub repo.
compile "com.polidea.rxandroidble:rxandroidble:1.2.4"
<dependency>
<groupId>com.polidea.rxandroidble</groupId>
<artifactId>rxandroidble</artifactId>
<version>1.2.4</version>
<type>aar</type>
</dependency>
If your are interested in cutting-edge build you can get a SNAPSHOT
version of the library.
NOTE: It is built from the top of the master
branch and a subject to more frequent changes that may break the API and/or change behavior.
To be able to download it you need to add Sonatype Snapshot repository site to your build.gradle
file:
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
Using RxAndroidBle enables you to unit test your application easily. For examples how to use mocking head to MockRxAndroidBle.
If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request.
When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. Please also make sure your code compiles by running ./gradlew clean checkstyle test
.
- Dariusz Seweryn (dariusz.seweryn@polidea.com)
- Paweł Urban (pawel.urban@polidea.com)
- Michał Zieliński (michal.zielinski@polidea.com)
- Fracturedpsyche (https://github.com/fracturedpsyche)
- Andrea Pregnolato (https://github.com/pregno)
- Matthieu Vachon (https://github.com/maoueh) - custom operations, yay!
Copyright 2016 Polidea Sp. z o.o
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.