如何在RX Java链中使用“if-else”?

2022-09-01 23:21:21

我是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();

有没有更简单的方法来实现这一目标?

谢谢!


答案 1

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个单独的流,并对每个流做出不同的反应,这可以通过多播你的可观察来完成,并从中创建2个不同的,一个将继续,例如,一个不会,每个可以有不同的订阅者逻辑。(您可以在这里阅读我关于多播的答案)getUserFromNetwork()ObservableObservablegetVipUserFromNetwork()


答案 2

我最近发现了switchIfEmpty运算符,它适合我的需求,可能对某些人有用。Rx对我来说仍然是一种新的思维方式,所以我也愿意接受建议和评论。让我试着给你另一种思考它的方法。正如@yosriz所指出的,使用 switchIfEmpty 和后续的 onComplete 是多余的。

顾名思义,switchIfEmpty 当基本值完成而不发出任何值时,它会切换到另一个可观察对象。

以下是以下两种情况:

  • 可观察对象发出一个值,然后完成
  • 可观察完成而不发出值。

诀窍是使用空流作为谓词。

给定用作谓词的基本可观察量,如果筛选其发射,则可以将 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() );

然后我们做一些逻辑,当他是VIP时,我们将isVip值传递到下游,如果用户不是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
    });

推荐