I have a rather long chain of Rx operations for my initial scan, connection and authorization. Here's the code.
fun startScanning() {
getScanObservable()
.take(1)
.map { scanResult ->
rxBleDevice = scanResult.bleDevice
observeDeviceState()
scanResult.bleDevice.establishConnection(false)
}
.flatMap { it }
.map { bleConnection ->
rxBleConnection = bleConnection
bleConnection.discoverServices()
}
.flatMapSingle { it }
.map { services ->
rxBleDeviceServices = services
performAuthentication()
}
.flatMap { it }
.subscribe({
state.postValue(State.AUTHENTICATED)
setupNotifications()
}, {
FirebaseCrashlytics.getInstance().recordException(it)
})
.let { disposables.add(it) }
}
So, to sum this up, the code takes the first scan result and immediately starts establishing connection. Once that's done, I discover services and then finally authenticate the mobile client. At the end of the "chain" (in the subscribe callback), I set up all of the needed characteristic notifications and then save the Disposable
into my CompositeDisposable
variable which also consists of all the Disposables that I get from subscribing to characteristic notifications.
When I call disposables.dispose()
, the client does in fact get disconnected. I know it because the peripheral shows disconnected status and RxBleDevice
shows Disconnected
status as well.
The problem is if I call the startScanning
method again, nothing happens. The first map
operation is never called and none of the subscribe methods are called as well. It only works if I restart the activity, which re-instantiates everything from scratch.
Here's also the getScanObservable()
code:
private fun getScanObservable(): Observable<ScanResult> {
val scanSettings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build()
val scanFilter = ScanFilter.Builder()
.setDeviceName(token.deviceUid)
.build()
return rxBleClient.scanBleDevices(scanSettings, scanFilter)
}
Here's how I dispose:
private val disposables = CompositeDisposable()
fun cleanup() {
if (!disposables.isDisposed) {
disposables.dispose()
rxBleDevice = null
rxBleDeviceServices = null
rxBleConnection = null
}
}
I call the cleanup()
method whenever I want to close the connection and prepare the device to rescan and reconnect. That's why I also destroy all of the RxAndroidBle references and only keep the RxBleClient reference.
The reason why you cannot start a new scan lays in how you dispose the previous one. If you would look into the implementation or CompositeDisposable.add()
Javadoc you would see that:
/** * Adds a disposable to this container or disposes it if the * container has been disposed. * @param disposable the disposable to add, not null * @return true if successful, false if this container has been disposed * @throws NullPointerException if {@code disposable} is null */
You call CompositeDisposable.dispose()
therefore disposing the container. If you would add an .doOnDispose { Log.i("startScanning", "disposed!") }
to the startScanning()
function you would quickly discovered that.
If you want to dispose contents of CompositeDisposable
but not the container use CompositeDisposable.clear()
:
/** * Atomically clears the container, then disposes all the previously contained Disposables. */