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();
これを達成するより簡単な方法はありますか?
ありがとう!
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つは継続しないもので、それぞれが異なるサブスクライバロジックを持つことができます。 (あなたは こちら マルチキャストに関する私の答えを読むことができます)
最近、switchIfEmpty演算子を見つけました。これは私のニーズに合っていて、一部の人にとっては便利かもしれません。 Rxはまだ新しい考え方であるため、提案やコメントも受け付けています。別の方法で考えてみましょう。 @yosrizが指摘したように、後続のonCompleteでswitchIfEmptyを使用することは冗長です。
名前が示すように、switchIfEmptyこれは、ベース1が値を出力せずに完了すると、別のオブザーバブルに切り替わります。
これらは2つのケースです。
トリックは、空のストリームを述部として使用することです。
述語として使用されるベースオブザーバブルを指定すると、その放出をフィルタリングする場合、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
});