Сцепленные вызовы RxJava в потоке пользовательского интерфейса

Я пытаюсь изучить некоторые RxJava и RxAndroid, и я думаю, что проблема, с которой я столкнулся, может быть легко решена с помощью такого инструмента. Вот проблема: у нас может быть 'N' количество просмотров в Activity, и каждый из них используется для выполнения некоторого условия. Когда пользователь нажимает «Сохранить», мы хотим проверить, соблюдены ли все условия во всех представлениях, и если нет, попросить пользователя подтвердить для каждого из них отдельно. Итак, вот пример того, как я справился бы с этой проблемой без 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);
}

Очевидно, у меня был бы какой-то прослушиватель для результата подтверждения, и после подтверждения условия (с помощью OK или Cancel) для «view.metCondition()» будет установлено значение true, поэтому оно больше не будет всплывать для этого представления. И, конечно же, после того, как «проверка» вернет true, будет запущена функция «Сохранить ()».

Это действительно грубая демонстрация моего реального решения, поскольку я хотел сделать его как можно более простым, поэтому просто прокомментируйте, если вы знаете, как что-то подобное можно сделать с помощью RxJava. Я уже использую библиотеку для некоторых асинхронных вещей (разговор с устройствами, подключенными к usb), поэтому я кое-что знаю, но никогда не знал, как связывать такие вызовы.

Любая помощь приветствуется.

Изменить

добавлен метод слушателя, чтобы мы могли видеть, что функция «validate()» вызывается снова


person Darko Smoljo    schedule 04.03.2016    source источник


Ответы (2)


Для цепной проверки вам действительно следует изучить combineLatest() оператор. Сначала вы создаете Observable для каждого View, а затем используете этот оператор. RxBinding — отличное расширение для представлений Android.

См. в этом примере. Это хорошо для проверки.

person razzledazzle    schedule 04.03.2016
comment
Это очень хороший пример того, что мне нужно, но мне потребовалось некоторое время, чтобы понять это. Большое спасибо. - person Darko Smoljo; 04.03.2016

Еще один пример для вдохновения :)

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);
}

В этом примере нам по-прежнему нужно вызывать метод validate каждый раз, когда мы хотим выполнить проверку.

Вот еще один пример, где мы не разрываем цепочку.

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
}

Извините, я не тестировал. Просто попытался дать вам основную идею различных подходов. Эти подходы в значительной степени основаны на идеях, уже выраженных в принятом ответе.

person MyDogTom    schedule 04.03.2016