My Android app for a BLE peripheral writes to 2 different device characteristics and receives notifications from 2 others. The developers of the RxAndroidBle
library caution against multiple subscriptions on the same RxBleConnection
instance, but I don't see any realistic way of combining all these I/O operations into a single .subscribe()
, especially since one of the notifications is a pretty constant data "firehose".
Not knowing any better, I have been simply storing the RxBleConnection
in a variable and using it in multiple .subscribe()
s. As far as I can tell, this has been working OK. I've investigated the RxAndroidBle
library's ConnectionSharingAdapter
but, though I have analyzed the code, I don't understand what benefits it offers over my simplistic approach (though I would love to know).
In general, some elaboration on how multiple .subscribe()
s introduce state, and the potential pitfalls, would be helpful. Questions:
RxBleConnection
in a variable and using it for multiple .subscribe()
s? ConnectionSharingAdapter
solve it? .subscribe()
(that won't degrade performance)? What is wrong with storing
RxBleConnection
in a variable and using it for multiple.subscribe()
s? (...) What does it mean to say that multiple subscriptions "introduce state", and how can this cause problems?
These two questions are almost the same.
An RxBleConnection
is an abstraction over a state in which a BLE client exchanges some handshake packets with a BLE server after which both the client and the server are considered connected. Unfortunately due to various reasons this connection can be broken at almost any time (and that happens quite often) which a single stored variable of RxBleConnection
cannot easily express in a reactive manner.
On the other hand observing RxBleDevice.establishConnection()
will propagate an error to the subscriber once the connection will get broken. Although none of the functionality that the emitted RxBleConnection
will work once the connection is broken - a saved variable of the connection will not inform about the problem when it happens.
So if a user saves an RxBleConnection
to a variable - it introduces a state in which the variable may be in and the user is responsible for propagating (clearing the variable) errors that may happen later. As an opposite subscribing to .establishConnection()
will emit an exception when the connection cannot be used.
As many of programmers may have noticed during their practice - managing state is the most common source of bugs in the applications. Reducing state is a way to mitigate the risk of bugs.
There is an excellent (but quite advanced) talk from Devoxx by Jake Wharton: Managing State with RxJava by Jake Wharton
If this is a problem, how does
ConnectionSharingAdapter
solve it?
The .establishConnection()
observable does not allow to have more than one simultaneous subsription because of the stateful nature of BLE connection and communication (request-response is a well known pattern) and having more than one .subscribe()
that uses the same connection may cause interference with the other without a clear trace in the code. That is why BleAlreadyConnectedException
was introduced for users that do not follow all places in the code that use an RxBleConnection
. ConnectionSharingAdapter
was introduced as a helper for users that conciously decide to share a single connection between more than one interactor. If the RxBleConnection
gets broken the ConnectionSharingAdapter
will propagate the error to all Subscriber
s.
Is there any clean way of combining all four characteristic I/O operations into a single
.subscribe()
(that won't degrade performance)?
In most situations it is possible to cleanly combine many I/Os. The above mentioned talk touches this topic. Proper combining multiple I/Os comes with a cost of additional allocations but it is rarely a problem when dealing with BLE because of the low speed of this communication channel.