1

I am using awesome rxandroidble library for BLE control.
I keep connection between activities. Before I start scanning, I want to disconnect all connected devices first. Sometimes It is not working if there are many connections. This is the solution I am using:

public void doScan() {

    if (isScanning()) return;

    // disconnect all connected devices first:
    while(BleController.getDefault().getDisconnectTriggerSubject().hasObservers()){
        BleController.getDefault().getDisconnectTriggerSubject().onNext(null);
    }

    scanSubscription = rxBleClient.scanBleDevices(
            new ScanSettings.Builder()
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                    .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                    .build(),
            new ScanFilter.Builder()
                    // add custom filters if needed
                    .build()
    )
            .filter(rxBleScanResult -> !TextUtils.isEmpty(rxBleScanResult.getBleDevice().getName()))
            .observeOn(AndroidSchedulers.mainThread())
            .doOnUnsubscribe(this::clearSubscription)
            .subscribe(resultsAdapter::addScanResult, this::onScanFailure);

    updateButtonUIState();

}

BleController is initialized with the main application's context and keeps the connectionObservable, disconnectTriggerSubject, rxBleClient.

What can be the better solution? Any help would be appreciated!

mudin
  • 2,672
  • 2
  • 17
  • 45

1 Answers1

2

From your post I can see that you are mixing the BLE scanning/connection logic with the UI/Activity logic. This may be a problem to manage connections correctly.

What you could do is to put all the BLE logic to your BleController which already has a good name but it seems that in your situation is rather a BleObjectsContainer.

For instance you could only expose from the BleController only observables that are fulfilling your specific use-cases in a way that the Activities do not need to handle. i.e. Your BleController could handle scanning:

private final BehaviorRelay<Boolean> isScanningPublishRelay = BehaviorRelay.create(false); // a relay (that cannot emit an error) that emits when a scan is ongoing

private Observable<ScanResult> scanDevicesWithNonNullNameObs = rxBleClient.scanBleDevices(new ScanSettings.Builder().build())
        .filter(scanResult -> !TextUtils.isEmpty(scanResult.getBleDevice().getName()))
        .doOnSubscribe(() -> isScanningPublishRelay.call(true)) // when scan is subscribed/started emit true
        .doOnUnsubscribe(() -> isScanningPublishRelay.call(false)) // when scan is stopped emit false
        .subscribeOn(AndroidSchedulers.mainThread()) // the above emissions will happen on the same thread. should be serialized
        .unsubscribeOn(AndroidSchedulers.mainThread()) // the above emissions will happen on the same thread. should be serialized
        .share(); // share this observable so no matter how many places in the code will subscribe the scan is started once and `isScanningPublishRelay` is called once

public Observable<ScanResult> scanDevicesWithNonNullName() { // getter for the scan observable
    return scanDevicesWithNonNullNameObs;
}

And besides of scanning it would also handle your specific use-cases for each Activity that needs it:

class ScanInProgress extends Throwable {
    // ...
}

public Observable<YourActivityUseCaseModel> doYourSpecificStuff(Observable<RxBleConnection> connectionObservable) {
    return Observable.merge(
            connectionObservable,
            isScanningPublishRelay
                    .takeFirst(aBoolean -> aBoolean)
                    .flatMap(ignored -> Observable.error(new ScanInProgress())) // this will only emit an error when a scan is ongoing
    )
            .flatMap(...); // do the rest of your stuff
}

This way in your activities you would only need to subscribe to whatever model they need and handle the BLE in a single place that is dedicated for it (BleController).

In the above example you need to provide the Observable<RxBleConnection> but it can be achieved in many different ways and could managed in BleController as well so it would not be exposed in the interface.

Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21
  • Thank you very much for your answer. It is very useful. I have never used `BehaviorRelay` and `Observable.merge()`. And yes, later I will refactor my code, all the ble controls will be inside of the controller, and any activity will just checks connection, scans, connects, writes command, .... and subscribes `EventBus` events to get notification from device, connection status,... – mudin Jan 17 '18 at 01:14
  • One more issue I have is sometimes it is scanning even scanning activity is stopped. what do you think? – mudin Jan 17 '18 at 01:22
  • 1
    That one (or more) `Subsrcription` to scanning was not unsubscribed – Dariusz Seweryn Jan 17 '18 at 07:13