web-dev-qa-db-ja.com

RXで「if-else」を使用する方法Java chain?

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

これを達成するより簡単な方法はありますか?

ありがとう!

12
niu yi

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を作成します。1つはたとえばgetVipUserFromNetwork()を継続し、もう1つは継続しないもので、それぞれが異なるサブスクライバロジックを持つことができます。 (あなたは こちら マルチキャストに関する私の答えを読むことができます)

17
yosriz

最近、switchIfEmpty演算子を見つけました。これは私のニーズに合っていて、一部の人にとっては便利かもしれません。 Rxはまだ新しい考え方であるため、提案やコメントも受け付けています。別の方法で考えてみましょう。 @yosrizが指摘したように、後続のonCompleteでswitchIfEmptyを使用することは冗長です。

名前が示すように、switchIfEmptyこれは、ベース1が値を出力せずに完了すると、別のオブザーバブルに切り替わります。

これらは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() );

次に、少しロジックを実行します。ユーザーがVIPでない場合、VIPの場合はisVip値をダウンストリームに渡します。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 observableは

  • 値を発行します。これは、フラットマップユーザーです
  • 何も出さずに完了

何も発行されない場合、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
    });
4
LookForAngular