0

I'm using RxAndroidBle for a project and I need to read some characteristics off of a ble device and then display them on screen. The code below works except for this::displayCharacteristics

private void readCharacteristics() {
    mConnectionObservable.flatMap(rxBleConnection -> Observable.combineLatest(
            rxBleConnection.readCharacteristic(UUID_SERIAL_NUMBER),
            rxBleConnection.readCharacteristic(UUID_MACHINE_TYPE),
            rxBleConnection.readCharacteristic(UUID_CHARACTERISTIC),
            ConnectedViewModel::new
            ).doOnError(this::logError)
    ).doOnError(this::logError)
    .subscribe(this::displayCharacteristics, this::logError);
}


private void displayCharacteristics(ConnectedViewModel model) {
    mSerialNumber.setText(model.getSerialNumber());
    mMachineType.setText(model.getMachineType());
    mCableLength.setText(model.getCableLength());
    mCableSize.setText(model.getCableSize());
    mUnits.setText(model.getUnits());
}

This is because of a CalledFromWrongThreadException when executing this::displayCharacteristics. What is the proper way to display the data on the thread that the fragment is on? Would I have to use a listener? Thanks!

David Carek
  • 1,103
  • 1
  • 12
  • 26

2 Answers2

2

You want to process your data in the IO thread and the emitted data back in the UI Thread.

You can do that by using those Schedulers.

private void readCharacteristics() {
    mConnectionObservable.flatMap(rxBleConnection -> Observable.combineLatest(
            rxBleConnection.readCharacteristic(UUID_SERIAL_NUMBER),
            rxBleConnection.readCharacteristic(UUID_MACHINE_TYPE),
            rxBleConnection.readCharacteristic(UUID_CHARACTERISTIC),
            ConnectedViewModel::new
            ).doOnError(this::logError)
    ).doOnError(this::logError)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(this::displayCharacteristics, this::logError);
}

You should also avoid using all those .doOnError() everywhere and process it in your subscriber (as you have done already)

Emanuel
  • 8,027
  • 2
  • 37
  • 56
  • Thanks for the tip about doOnError(). I thought that the errors did not get propagated up to the subscribe throwable but it appears I was wrong. – David Carek Sep 21 '17 at 12:48
  • 1
    Usually its used to debug (at least in most cases) during flatmapping other Observables. You should always consider to process errors in your subscribe method. If an Observable before fails due an error you will also get it in your subscribe. – Emanuel Sep 21 '17 at 12:50
0

For interacting with the UI on the Android OS you need to be on the main thread. When dealing with RxJava you can do that easily by adding a line .observeOn(AndroidSchedulers.mainThread()).

As of version 1.4.0 RxAndroidBle does not have a transitive dependency on the RxAndroid library any longer. To use AndroidSchedulers class you need to add a dependency yourself in the build.gradle file:

dependencies {
    compile 'io.reactivex:rxandroid:1.2.1'
}

The RxAndroidBle library handles threading of the BLE commands on it's own so there is little need to use .subscribeOn(Schedulers.io()) as the execution will be quickly handovered to a background thread.

Your code could look like this to work with the UI:

private void readCharacteristics() {
    mConnectionObservable.flatMap(rxBleConnection -> Observable.combineLatest(
            rxBleConnection.readCharacteristic(UUID_SERIAL_NUMBER),
            rxBleConnection.readCharacteristic(UUID_MACHINE_TYPE),
            rxBleConnection.readCharacteristic(UUID_CHARACTERISTIC),
            ConnectedViewModel::new
        )
        .doOnError(this::logError)
    )
    .doOnError(this::logError)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(this::displayCharacteristics, this::logError);
}
Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21