androidrx-androidrxandroidble

Can't restart scan after disposing of the first connection


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.


Solution

  • 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.
     */