Eve Eve - 1 month ago 7
Android Question

RxJava operator like amb, but only with valid results

I want to automatically find a device in an

Android
app. Therefore I would like to make two calls, a network call with
Retrofit
and a non-network call using a custom SDK, at the same time to find out which device the user is using. The app should pick the first result that delivers a valid value.

I use
RxJava
and tried it with the operator
amb
like this:

public Observable<LoginResponse> detectDevice(String username, String pwd) {
return Observable.amb(device1.login(username, pwd), device2.login(username, pwd));
}


This seems to work fine if the device that needs to be detected is device1, which uses a network call. But if it is device2 that should be detected, it will return
onError()
, because the
device1.login()
finishes faster and
amb
takes the first
onNext()
or
onError()
. Even though device2.login() delivers a valid result, it will not be taken into account, because it is too slow.

My question is: Is there a better way to only take a valid response or another operator? I don't want to use
zip
, because in the future there might be more devices and I don't want to let the user wait until the login request was finished for each device.

Eve Eve
Answer

JohnWowUs post inspired me to use materialize, but a bit different, this is what I went with:

public Observable<LoginResponse> detectDevices(String username, String password) {

    Observable<Notification<LoginResponse>> deviceOneObservable = device1.login(username, password).timeout(2, TimeUnit.SECDONDS).materialize().take(1);
    Observable<Notification<LoginResponse>> deviceTwoObservable = device2.login(username, password).timeout(2, TimeUnit.SECONDS).materialize().take(1);

    return Observable
            .zip(deviceOneObservable, deviceTwoObservable, new Func2<Notification<LoginResponse>, Notification<LoginResponse>, Pair<Notification<LoginResponse>, Notification<LoginResponse>>>() {
                @Override
                public Pair<Notification<LoginResponse>, Notification<LoginResponse>> call(Notification<LoginResponse> loginResponseNotification, Notification<LoginResponse> loginResponseNotification2) {
                    return Pair.create(loginResponseNotification, loginResponseNotification2);
                }
            })
            .flatMap(new Func1<Pair<Notification<LoginResponse>, Notification<LoginResponse>>, Observable<LoginResponse>>() {
                @Override
                public Observable<LoginResponse> call(Pair<Notification<LoginResponse>, Notification<LoginResponse>> notificationNotificationPair) {

                    final Notification<LoginResponse> deviceOneNotification = notificationNotificationPair.first;
                    final Notification<LoginResponse> deviceTwoNotification = notificationNotificationPair.second;

                    //treat 4 different cases of device detection
                    //case1: no compatible device was detected
                    if (deviceOneNotification.isOnError() && deviceTwoNotification.isOnError()) {
                        return Observable.just(new LoginResponse(DeviceType.UNKNOWN));

                        //case2: device1 was detected
                    } else if (deviceOneNotification.isOnNext()) {
                        return Observable.just(new LoginResponse(DeviceType.DEVICE_ONE));

                       //case3:  device2 was detected
                    } else if (deviceTwoNotification.isOnNext()) {
                        return Observable.just(new LoginResponse(DeviceType.DEVICE_TWO));
                       //case4:  error has occurred
                    } else {
                        ... //error handling
                    }
                }
            }
}