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

23

我是一个RXJava/RXAndroid的新手。我想实现这个案例:根据一些条件选择不同的方法在RXJava中。例如,首先,我从网络获取用户信息,如果这是一个VIP用户,我将继续从网络获取更多信息或者只在主线程中显示一些信息(打破链条)。这里是流程图:https://istack.dev59.com/0hztR.webp

我对此进行了一些搜索,只发现 "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
这就是你能做到的最好了。你的代码没有任何问题。 - Amir Ziarati
2个回答

23

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

在这种方法中,您有一个单一的流程,没有中途的“副作用”。

如果您的处理方式完全不同(这不是这种情况),那么可以将流分成两个独立的流,并对每个流做出不同的反应,这可以通过将您的 getUserFromNetwork() 可观察对象广播到两个不同的 Observable 来实现,一个会继续执行例如 getVipUserFromNetwork(),另一个则不会,并且每个都可以具有不同的订阅逻辑。 (您可以在这里阅读有关多路广播的答案)


7
我最近发现了 switchIfEmpty 操作符,它适合我的需求,也可能对其他人有用。Rx 对我来说仍然是一种新的思维方式,所以我也愿意听取建议和评论。 让我试着给你另一种思考方式。正如 @yosriz 指出的那样,使用 switchIfEmpty 和随后的 onComplete 是多余的。
就像名字所说的,switchIfEmpty 在基本 Observable 完成而没有发出任何值时切换到另一个 Observable。
这里有两种情况:
  • Observable 发出一个值,然后完成
  • 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() );

然后我们进行一些逻辑处理,当用户是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
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接