Darko Smoljo
Darko Smoljo

Reputation: 435

Chained RxJava calls on UI thread

I'm trying to learn some RxJava and RxAndroid and I think the problem I have can be easily done with such tool. Here is the problem: We could have 'N' number of views in an Activity, and every one is used to met some condition. When the user presses 'Save' we want to check if all the conditions are met in all the views, and if not, ask the user to confirm for each one separately. So here is the example of how I would deal with this problem without the RxJava:

private void validation(List<CustomView> views)
{
    for (CustomView view : views)
    {
        if (view.metCondition() == false)
        {
            showConfirmationDialog(view);
            return false;
        }
    }

    return true;
}


private void showConfirmationDialog(CustomView view)
{
    ConfirmationDialog dialog = new ConfirmationDialog(this, view);

    dialog.show();
}

private void dialogResult(CustomView view)
{
    view.setCondition(true);

    validation(mViews);
}

Obviously I'd have some sort of listener for confirmation result and after the condition is confirmed (with OK or Cancel) the "view.metCondition()" would be set to true so it would not pop for this view again. And of course, after the "validation" would return true it would run the "Save()" function.

This is really ruff showing of my real solution as I wanted to keep it as simple as possible, so just comment if you know how something like this could be done with RxJava. I'm already using the library for some asynchronous stuff (talking to usb connected devices) so I know something, but never knew how to chain the calls like this.

Any help is greatly appreciated.

Edit

added the listener method so we could see that the "validate()" function is called again

Upvotes: 1

Views: 448

Answers (2)

MyDogTom
MyDogTom

Reputation: 4606

Just one more example for inspiration :)

private static class CustomViewValidator {
    //Subject can be attach to other sources eg. EditText etc
    //Of course it can be replaced with simple variable
    BehaviorSubject<Boolean> mSubject = BehaviorSubject.create();

    Observable<Boolean> getValidationObservable() {
        return mSubject.asObservable().map(s -> {
            if (!s) {
                throw new ViewValidationThrowable(CustomViewValidator.this);
            } else {
                return true;
            }
        });
    }

    void setCondition(boolean v) {
        mSubject.onNext(v);
    }
}

private static class ViewValidationThrowable extends RuntimeException {
    //custom Exception let us to keep reference to invalid View
    private final CustomViewValidator mView;

    private ViewValidationThrowable(CustomViewValidator view) {
        mView = view;
    }
}

private List<CustomViewValidator> mViews;

private void validate(final List<CustomViewValidator> viewObservables) {

    Observable.from(viewObservables)
            .flatMap(CustomViewValidator::getValidationObservable)
            .subscribe(aBoolean -> {
                        //we can just ignore all items
                    },
                    throwable -> {
                        if (throwable instanceof ViewValidationThrowable) {
                            CustomViewValidator view = ((ViewValidationThrowable) throwable).mView;
                            //show dialog here
                        }
                    },
                    () -> {
                        //everything valid
                    });
}

private void dialogResult(CustomViewValidator view) {
    view.setCondition(true);
    validate(mViews);
}

In this example we still need to call validate method each time we want to do validation.

Here is another example where we do not break chain.

private static class Pair<T,V> {
    private final T first;
    private final V second;

    public Pair(T first, V second) {
        this.first = first;
        this.second = second;
    }
}
private static class CustomViewValidator {
    //Subject allows us:
    //  * probably not break chain later using some retry techniques
    //  * subject can be attach to other sources eg. EditText etc
    //Of course it can be replaced with simple variable
    BehaviorSubject<Boolean> mSubject = BehaviorSubject.create();

    Observable<Pair<Boolean,CustomViewValidator>> getValidationObservable() {
        return mSubject.asObservable().map(s -> new Pair<>(s,CustomViewValidator.this));
    }

    void setCondition(boolean v) {
        mSubject.onNext(v);
    }
}

private void validate(final List<Observable<Pair<Boolean, CustomViewValidator>>> viewObservables) {
    //IMPORTANT do not forget to unsubscribe
    // In this case we do not break our chain, so it can last forever
    Subscription subsciption = Observable.combineLatest(viewObservables,
            objects -> {
                for (Object object : objects) {
                    Pair<Boolean, CustomViewValidator> viewPair = (Pair<Boolean, CustomViewValidator>) object;
                    if (!viewPair.first) {
                        return viewPair;
                    }
                }
                return new Pair<>(true, null);
            })
            .subscribe(pair -> {
                if (pair.first) {
                    //everything is valid DO NOT USE second argument here
                } else {
                    //show dialog here using pair.second as View
                }
            });

}

private void dialogResult(CustomViewValidator view) {
    view.setCondition(true);
    //no reason to call validate again
    //setCondition will trigger chain again
}

Sorry, I didn't tested it. Just tried to give you the main idea of different approaches. These approaches are heavily based on ideas already expressed in accepted answer.

Upvotes: 0

razzledazzle
razzledazzle

Reputation: 6930

For chained validation, you should really look into the combineLatest() operator. First you create Observable for every View and then use that operator. RxBinding is an excellent extension for Android views.

See this example. It's a good one for validation.

Upvotes: 3

Related Questions