map()
演算子についても知りました。 まだ、このフレームワークを終了して使用を開始する衝動を感じていない人々を理解できます。条件付きで言えば、氷山の一角にすぎません。 しかし、すぐにすべてが変更されます-RxJavaのパワーのほとんどは、その演算子に隠されています。私は、それらの一部を学習できる例を用意しました。
挑戦する
次のようなメソッドがあるとします:
// url', - Observable<List<String>> query(String text);
テキストを検索および表示するシステムを作成したい。 前のレッスンで学んだことに基づいて、次のように書くことができます。
query("Hello, world!") .subscribe(urls -> { for (String url : urls) { System.out.println(url); } });
データストリームを変換する機能が失われているため、このソリューションは私たちを決して満足させません。 各 URLを変更したい場合は、
Subscriber
でこれらすべてを実行する必要があります。したがって、
map()
したすべてのトリックは機能しません。
1つのURLリストで機能する
map()
を記述し、変更されたURLのリストを表示することは可能ですが、この場合、
map()
各呼び出しにはfor-eachが含まれます。 また、あまり美しくありません。
希望のきらめき
メソッド
Observable.from()
適用して、コレクションを
Observable.from()
、このコレクションの要素を次々に「放出」します。
Observable.from("url1", "url2", "url3") .subscribe(url -> System.out.println(url));
必要なもののように見えるので、タスクで使用してみましょう。
query("Hello, world!") .subscribe(urls -> { Observable.from(urls) .subscribe(url -> System.out.println(url)); });
サイクルを取り除きましたが、最後に起こったことは完全な混乱のように見えます:サイクルの代わりに、サブスクリプションが互いに埋め込まれました。 そして、コードが混乱しているように見えるのは悪いことであるだけでなく、変更するのが難しいでしょう。 まだ言及していないRxJavaの機能と競合します1 。 うーん。
より良い方法はありますか
救い主の姿を見て息を止めてください:
flatMap()
Observable.flatMap()
は、ある
Observable
によって
Observable
された入力を受け取り、別の
Observable
によって
Observable
されたデータを返します。 いわば、イベントの予想外の変化:あるデータストリームを取得していると思っていたが、実際には別のデータストリームを取得していた。
flatMap()
が問題の解決にどのように役立つかを以下に示します。
query("Hello, world!") .flatMap(new Func1<List<String>, Observable<String>>() { @Override public Observable<String> call(List<String> urls) { return Observable.from(urls); } }) .subscribe(url -> System.out.println(url));
何が起こっているのかを理解しやすくするためにフルバージョンを示しましたが、ラムダを使用してコードを書き換えると、問題なく見えるようになります。
query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .subscribe(url -> System.out.println(url));
考えてみると、かなり奇妙なことです。
flatMap()
が別の
Observable
返すの
flatMap()
なぜですか? ここで重要
Observable
は、新しい
Observable
が
Subscriber
に最終的に表示されることです。
List<String>
は受け取りません
Observable.from()
から受け取るように、個々の
String
オブジェクトのストリームを受け取ります。
ちなみに、この瞬間は私にとって最も難しいように思えましたが、理解してすぐに気づくと、RxJavaの動作のほとんどが私の頭の中で決まった場所に落ちました。
そして、あなたはもっとクールなことができます
それは重要です
flatMap()
は、必要
Observable
を返すことができます。
たとえば、2番目の方法があります。
// , null, 404 Observable<String> getTitle(String URL);
URLを印刷する代わりに、アクセスできる各サイトのタイトルを印刷したいと思います。 問題があります。私のメソッドは1つのURLのみを受け入れ、
String
を返さず、
String
を返す
Observable
を返します。
これらの問題は両方とも
flatMap()
で解決できます。 最初に、urlのリストから個々のurlのストリームに移動し、次に
getTitle()
内で
flatMap()
から、最終結果を
Subscriber
に渡します。
query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(new Func1<String, Observable<String>>() { @Override public Observable<String> call(String url) { return getTitle(url); } }) .subscribe(title -> System.out.println(title));
もう一度、ラムダですべてを単純化します。
query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .subscribe(title -> System.out.println(title));
すごいね
Observables
返すいくつかの独立したメソッドを組み合わせます。
2つのAPI呼び出しを1つのチェーンにまとめた方法に注目してください。 同じことが、APIの任意の数の呼び出しに対して実行できます。 おそらく、必要な結果を得るためにいくつかのAPI呼び出しの作業を調整することがどれほど難しいかご存知でしょう。リクエストを作成し、コールバック関数で結果を取得し、内部から新しいリクエストを作成しました... Brr。 そしてここでは、この地獄をすべて回避して、すべての同じロジックを1つの短いリアクティブコールに入れました2 。
豊富なオペレーター
これまで、2つの演算子のみを見てきましたが、実際にはRxJavaにはさらに多くの演算子があります。 他にどのようにコードを改善できますか?
getTitle()
は、404エラーが発生した場合に
null
を返し
null
。
"null"
を表示したくないため、不要な値を除外できます。
query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .filter(title -> title != null) .subscribe(title -> System.out.println(title));
filter()
は、テストに合格した場合にのみ、受信したのと同じデータストリーム要素を「放出」します。
そして今、私たちは5つ以上の結果を表示したくないのです。
query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .filter(title -> title != null) .take(5) .subscribe(title -> System.out.println(title));
take()
は、指定された要素数以下を返します(この場合、要素が5未満の場合、
take()
は単に作業を早く終了します。
また、取得したすべてのタイトルをディスクに保存しましょう。
query("Hello, world!") .flatMap(urls -> Observable.from(urls)) .flatMap(url -> getTitle(url)) .filter(title -> title != null) .take(5) .doOnNext(title -> saveTitle(title)) .subscribe(title -> System.out.println(title));
doOnNext()
使用すると、新しいデータ項目を取得するたびに発生するアクションを追加できます。この場合、このアクションはヘッダーを保存します。
データの流れを操作することがどれほど簡単かを見てみましょう。 レシピにますます多くの新しい材料を追加でき、消化できないブルダに陥ることはありません。
RxJavaにはワゴンとさまざまなオペレーターの小さな台車が付属しています。 このような膨大なリストは恐ろしいかもしれませんが、少なくとも何が利用可能かを知るには、見る価値があります。 使用可能なオペレーターを思い出すには少し時間がかかりますが、これを行うとすぐに指先で真の力を得ることができます。
ああ、ところで、あなたはあなた自身の演算子を書くこともできます! このトピックはこのシリーズの記事の範囲を超えていますが、このようにしましょう。独自の演算子を考え出せば、ほぼ確実に実装できます3 。
それでは、次は何ですか?
まあ、私はあなたが懐疑的であり、あなたを再び説得するためにうまくいかなかったことを理解しています。 なぜこれらすべての演算子に煩わされるのでしょうか?
アイデア#3:演算子を使用すると、データストリームで必要なことを何でも実行できます
唯一の制限はあなた自身です。
単純な演算子のチェーン以外を使用せずに、かなり複雑なデータ操作ロジックを作成できます。 これは機能的なリアクティブプログラミングです。 頻繁に使用すればするほど、プログラムコードの見た目を変えるようになります。
また、データを変換した後、エンドユーザーにデータを提示することがどれほど簡単かを考えてください。 この例の最後で、APIに対して2つのリクエストを行い、データを処理し、同時にそれらをディスクに保存しました。 しかし、最後の
Subscriber
はそれについては
Subscriber
ません。通常の
Observable<String>
で引き続き機能します。 カプセル化によりコードが簡単になります!
3番目の部分では、RxJavaのその他のクールな機能について説明します。これらの機能は、データ操作にあまり関係していません。エラー処理と同時実行性です。
3番目の部分に進みます。
1したがって、たとえば、RxJavaでのエラー処理、マルチスレッド化、およびサブスクライブ解除は、このコードと少しばかり組み合わされます。 これらのトピックについては、第3部で説明します。
2ここで、コールバック地獄のもう一方の側面、つまりエラー処理について考えたことがあるかもしれません。 これについては、第3部で説明します。
3独自の演算子を作成する場合は、 こちらをご覧ください 。 ただし、実装の詳細については、記事の第3部を読む前に理解するのは非常に困難です。