Grokai RxJava、パート2:演算子

最初の部分では、RxJavaの主要な構成要素を調べ、 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は、新しいObservableSubscriberに最終的に表示されることです。 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部を読む前に理解するのは非常に困難です。

Source: https://habr.com/ru/post/J265583/


All Articles