Как использовать if-else в цепочке RX java?

Я новичок в RXJava/RXAndroid. Я хочу реализовать этот случай: выбрал другой способ, основанный на каком-то условии в RXJava. Например, сначала я получаю информацию о пользователе из сети, и если это пользователь VIP, я продолжаю получать дополнительную информацию из сети или просто показываю некоторую информацию в основном потоке (разрыв цепочки). Вот блок-схема: https://i.stack.imgur.com/0hztR.png

Я выполняю поиск по этому вопросу и нахожу, что может помочь только «switchIfEmpty». Я пишу следующий код:

getUserFromNetwork("userId")
                .flatMap(new Function<User, ObservableSource<User>>() {
                    @Override
                    public ObservableSource<User> apply(User user) throws Exception {
                        if(!user.isVip){
                            //show user info on MainThread!
                            return Observable.empty();
                        }else{
                            return getVipUserFromNetwork("userId");
                        }
                    }
                }).switchIfEmpty(new ObservableSource<User>() {
                    @Override
                    public void subscribe(Observer<? super User> observer) {
                        //show user info in main thread
                        //just break the chain for normal user
                        observer.onComplete();
                    }
                }).doOnNext(new Consumer<User>() {
                    @Override
                    public void accept(User user) throws Exception {
                        //show vip user info in main thread
                    }
                }).subscribe();

Есть ли более простой способ добиться этого?

Спасибо!


person niu yi    schedule 13.04.2017    source источник
comment
это лучшее, что вы можете сделать. в вашем коде нет ничего плохого.   -  person Amir Ziarati    schedule 13.04.2017


Ответы (2)


flatMap() — хороший выбор, с ним можно разделить поток, но в конце поток сливается воедино (все выбросы из каждого наблюдаемого разделения перетекают в основной поток). В вашем коде switchIfEmpty() является избыточным, так как это именно то, что делает Observable.empty() (немедленно вызывает onCompleted()), а также вам, конечно, нужен наблюдатель, если вы хотите, чтобы отображение происходило в основном потоке, но в любом случае, я думаю, что это нехорошо попрактикуйтесь, чтобы справиться с этим в середине потока.

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

getUserFromNetwork("userId")
            .flatMap(new Function<User, ObservableSource<User>>() {
                @Override
                public ObservableSource<User> apply(User user) throws Exception {
                    if (!user.isVip) {
                        return Observable.just(user);
                    } else {
                        return getVipUserFromNetwork("userId");
                    }
                }
            })
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(user -> {
                if (user.isVip){
                    //display vip user
                }else{
                    //display regular user
                }
            });

в этом подходе у вас есть единый поток без «побочных эффектов» в середине потока.

В случае, если вы обрабатываете совершенно другое (что не так), вы можете разделить поток на 2 отдельных потока и реагировать на каждый по-разному, это можно сделать с помощью многоадресной рассылки вашего getUserFromNetwork() Observable, и из этого Observable создать 2 разные Observable, один будет продолжаться, например getVipUserFromNetwork(), а другой нет, и у каждого может быть разная логика подписчика. (вы можете прочитать здесь мой ответ относительно многоадресной рассылки)

person yosriz    schedule 13.04.2017

Недавно я узнал об операторе switchIfEmpty, который подходит мне и может быть полезен для некоторых. Rx по-прежнему является для меня новым способом мышления, поэтому я также открыт для предложений и комментариев. Позвольте мне попытаться дать вам другой способ думать об этом. Как указал @yosriz, использование switchIfEmpty с последующим onComplete является излишним.

Как следует из названия, switchIfEmpty переключается на другой наблюдаемый объект, когда базовый завершается без выдачи какого-либо значения.

Это 2 случая:

  • Observable выдает значение, а затем завершается
  • Observable завершается без выдачи значения.

Хитрость заключается в использовании пустого потока в качестве предиката.

Учитывая базовую наблюдаемую, используемую в качестве предиката, если вы отфильтруете ее выброс, вы можете связать оператор switchIfEmpty с вашим резервным потоком.

В следующем коде «Пользователь» и «VIP-пользователь» используют один и тот же интерфейс/класс. Обратите внимание, что даже если я использую Java 8 Lambdas для написания кода, здесь нет операторов IF.

  // User Observable, cached so only 1 network call is done
Observable<User> user = getUserFromNetwork("USER_ID").cache();
  // This observable is the user's VIP Status as a boolean stream
Observable<Boolean> isVip = user.map(u -> u.isVip() );

Затем мы делаем немного логики, мы передаем вниз по течению значение isVip, когда он является VIP, если пользователь не является VIP, flatMap не будет оцениваться.

Observable<User> vipUser = isVip
    // If VIP emit downstream
    .filter(vip -> vip)
    // This flatmap is ignored if 
    // the emission is filtered out ( vip -> vip == false )
    .flatMap(vip -> user.flatMap(usr -> {
        return getVipUserFromNetwork(usr.getId());
    }));
});

В этот момент наблюдаемый объект vipUser может

  • Выдать значение, которое представляет собой пользователя с плоской картой
  • Ничего не излучать и завершить

Когда ничего не испускается, switchIfEmpty вызовет другой наблюдаемый

vipUser.switchIfEmpty(user)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(usr -> {
        // Logging the class just to understand the outcome
        System.out.println("User instanceOf " + usr.getClass());
    });

Вот полный код

Observable<User> user = getUserFromNetwork("USER_ID").cache();
Observable<Boolean> isVip = user.map(u -> u.isVip() );

Observable<User> vipUser = isVip
    .filter(vip -> vip)
    .flatMap(vip -> user.flatMap(usr -> {
        return getVipUserFromNetwork(usr.getId());
    }));
});

vipUser.switchIfEmpty(user)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(usr -> {
        // Handle UI Changes
    });
person LookForAngular    schedule 12.03.2018