これは、Android で RxJava を使用する実際の有用な例を含むリポジトリです。通常は、一定の「作業中」(WIP) 状態になります。
また、このリポジトリにリストされている例の多くを使用して、Rx の学習についても講演してきました。
using
)一般的な要件は、長時間にわたる I/O 集中型の操作をバックグラウンド スレッド (非 UI スレッド) にオフロードし、完了時に結果を UI/メイン スレッドにフィードバックすることです。これは、長時間実行される操作をバックグラウンド スレッドにオフロードする方法を示すデモです。操作が完了したら、メインスレッドに戻ります。すべて RxJava を使用しています。これは AsyncTasks の代替として考えてください。
長時間の操作は、ブロッキング Thread.sleep 呼び出しによってシミュレートされます (これはバックグラウンド スレッドで実行されるため、UI が中断されることはありません)。
この例が輝くのを実際に見るために。ボタンを複数回押して、ボタンのクリック (UI 操作) がブロックされないことを確認します。これは、長い操作はバックグラウンドでのみ実行されるためです。
これは、「バッファ」操作を使用してイベントを蓄積する方法のデモです。
ボタンが提供され、そのボタンのクリック数が一定期間にわたって蓄積され、最終結果が出力されます。
ボタンを 1 回押すと、ボタンが 1 回押されたことを示すメッセージが表示されます。 2 秒以内に 5 回連続してボタンを押すと、そのボタンを 5 回押したというログが 1 つ取得されます (「ボタンを 1 回押した」というログが 5 つあるのに対して)。
注記:
一定期間内のタップ数だけではなく「連続」タップを蓄積する、より確実なソリューションをお探しの場合は、 publish
オペレーターとbuffer
オペレーターの組み合わせが使用されている EventBus デモをご覧ください。より詳細な説明については、このブログ投稿も参照してください。
これは、最後のイベントのみが尊重される方法でイベントをどのように飲み込むことができるかを示すデモです。この典型的な例は、インスタント検索結果ボックスです。 「Bruce Lee」という単語を入力するときに、B、Br、Bru、Bruce、Bruce、Bruce L ... などの検索を実行するのではなく、賢明に数分間待ち、ユーザーが単語全体を入力し終えてから、「ブルース リー」を 1 回叫びます。
入力ボックスに入力すると、入力文字が変更されるたびにログ メッセージが出力されるのではなく、最後に出力されたイベント (つまり入力) だけが選択され、それが記録されます。
これは、RxJava の debounce/throttleWithTimeout メソッドです。
Square の Retrofit は、簡単なネットワーキングに役立つ素晴らしいライブラリです (まだ RxJava を試していない場合でも、ぜひチェックしてみてください)。 RxJava ではさらにうまく機能します。これらは、Netflix での Android の半神開発者 Jake Wharton の講演から直接抜粋した、GitHub API をヒットする例です。このリンクから講演を視聴できます。ちなみに、私が RxJava を使おうと思ったきっかけは、Netflix でのこの講演に参加したことです。
(注: GitHub API クォータにすぐに達してしまう可能性が高いため、これらの例を頻繁に実行し続ける場合は、パラメータとして OAuth トークンを送信してください)。
ビューの自動更新は非常に便利です。以前に Angular JS を扱ったことがあるなら、Angular JS には「双方向データ バインディング」と呼ばれる非常に気の利いた概念があるため、HTML 要素がモデル/エンティティ オブジェクトにバインドされると、そのエンティティの変更を常に「リッスン」し、モデルに基づいて状態を自動更新します。この例の手法を使用すると、プレゼンテーション ビュー モデル パターンのようなパターンを非常に簡単に使用できる可能性があります。
ここでの例はかなり初歩的なものですが、 Publish Subject
使用してダブル バインディングを実現するために使用されるテクニックは、さらに興味深いものです。
これは、RxJava Scheduler を使用したポーリングの例です。これは、サーバーを継続的にポーリングし、場合によっては新しいデータを取得したい場合に便利です。ネットワーク呼び出しは「シミュレート」されるため、結果の文字列を返す前に遅延が発生します。
これには 2 つのバリエーションがあります。
2 番目の例は、基本的に指数バックオフの変形です。
ここでは RetryWithDelay を使用する代わりに、RepeatWithDelay を使用します。 Retry(When) とRepeat(When) の違いを理解するには、このテーマに関する Dan の素晴らしい投稿をお勧めします。
repeatWhen
を使用せずに遅延ポーリングを行う別のアプローチは、連鎖的にネストされた遅延オブザーバブルを使用することです。 ExponentialBackOffFragment の例の startExecutingWithExponentialBackoffDelay を参照してください。
指数バックオフは、特定の出力からのフィードバックに基づいてプロセスの速度を変更する戦略です (通常は、再試行回数を減らすか、特定のプロセスを再試行または再実行するまでの待機時間を増やします)。
この概念は、例を挙げて理解することができます。 RxJava を使用すると、そのような戦略の実装が (比較的) 簡単になります。アイデアを提案してくれたマイクに感謝します。
ネットワーク障害が発生したとします。賢明な戦略は、ネットワーク呼び出しを 1 秒ごとに再試行し続けないことです。代わりに、遅延を増やして再試行する方が賢明です (いや... エレガントです!)。では、サイコロを使わずに 1 秒目にネットワーク呼び出しを実行しようとしますか? 10秒後に試してください...否定的ですか? 20 秒後に試してください。クッキーがありませんか? 1分後に試してください。これでもまだ失敗する場合は、ネットワークをあきらめる必要がありますよ!
RxJava とretryWhen
演算子を使用して、この動作をシミュレートします。
RetryWithDelay
コード スニペットの提供:
非常によく似た指数バックオフ メカニズムを使用するポーリングの例も見てください。
指数関数的バックオフ戦略の別の変形は、指定された回数だけ、遅延された間隔で操作を実行することです。つまり、1 秒後に特定の操作を実行し、10 秒後に再度実行し、20 秒後にその操作を実行します。合計 3 回実行すると、実行が停止します。
この動作のシミュレーションは、実際には以前の再試行メカニズムよりもはるかに簡単です。これを実現するには、 delay
演算子のバリアントを使用できます。
.combineLatest
を使用)断片的なポッドキャスト、エピソード #4 (4 分 30 秒あたり) でこのアイデアを提供してくれた Dan Lew に感謝します。
.combineLatest
すると、単一の場所で複数のオブザーバブルの状態を一度にコンパクトに監視できます。この例では、 .combineLatest
を使用して基本的なフォームを検証する方法を示しています。このフォームが「有効」であるとみなされるには、主に 3 つの入力があります (電子メール、パスワード、番号)。すべての入力が有効になると、フォームが有効になります (下のテキストが青に変わります:P)。そうでない場合は、無効な入力に対してエラーが表示されます。
各フォームフィールドのテキスト/入力の変更を追跡する 3 つの独立したオブザーバブルがあります (RxAndroid のWidgetObservable
テキストの変更を監視するのに便利です)。 3 つの入力すべてからイベントの変更が検出された後、結果が「結合」され、フォームの有効性が評価されます。
有効性をチェックするFunc3
関数は、3 つの入力すべてがテキスト変更イベントを受け取った後にのみ開始されることに注意してください。
この手法の価値は、フォーム内の入力フィールドの数が増えるほど明らかになります。それ以外の方法で多数のブール値を使用して処理すると、コードが乱雑になり、従うのが難しくなります。しかし、 .combineLatest
を使用すると、すべてのロジックがコンパクトなコード ブロックに集中します (私はまだブール値を使用していますが、これは例を読みやすくするためです)。
ディスク (高速) キャッシュとネットワーク (フレッシュ) 呼び出しの 2 つのソース Observable があります。通常、ディスク Observable はネットワーク Observable よりもはるかに高速です。ただし、動作を実証するために、オペレーターがどのように動作するかを確認するためだけに、偽の「遅い」ディスク キャッシュも使用しました。
これは 4 つの手法を使用して実証されます。
.concat
.concatEager
.merge
.publish
セレクター + マージ + takeUntilおそらく 4 番目のテクニックが最終的に使用したいものですが、テクニックの進行をたどってその理由を理解するのは興味深いことです。
concat
素晴らしいです。最初の Observable (この場合はディスク キャッシュ) から情報を取得し、次に後続のネットワーク Observable から情報を取得します。おそらくディスク キャッシュの方が高速であるため、すべてが正常に表示され、ディスク キャッシュが高速にロードされ、ネットワーク呼び出しが終了すると、「新しい」結果が交換されます。
concat
の問題は、最初の Observable が完了するまで、後続の Observable が開始されないことです。それは問題になる可能性があります。すべてのオブザーバブルが同時に開始され、期待どおりの結果が得られるようにしたいと考えています。ありがたいことに、RxJava はまさにそれを行うconcatEager
を導入しました。これは両方の Observable を開始しますが、前の Observable が終了するまで後者の Observable からの結果をバッファリングします。これは完全に実行可能なオプションです。
ただし、すぐに結果の表示を開始したい場合もあります。最初のオブザーバブルが (何らかの奇妙な理由で) すべてのアイテムを実行するのに非常に時間がかかると仮定すると、たとえ 2 番目のオブザーバブルの最初のいくつかのアイテムがネットワークに到達したとしても、強制的にキューに入れられます。必ずしも Observable を「待機」する必要はありません。このような状況では、 merge
演算子を使用できます。アイテムが放出されるときに、アイテムをインターリーブします。これは非常にうまく機能し、結果が表示されるとすぐに結果を吐き出し始めます。
concat
演算子と同様に、最初の Observable が常に 2 番目の Observable よりも高速であれば、問題は発生しません。ただし、 merge
の問題は、何らかの奇妙な理由で、新しい/新しいオブザーバブルの後にキャッシュまたは遅いオブザーバブルによってアイテムが発行された場合、新しいコンテンツが上書きされることです。この例の「MERGE (SLOWER DISK)」ボタンをクリックして、この問題の動作を確認してください。 @JakeWharton と @swankjesse の貢献は 0 になります!現実の世界では、新しいデータが古いディスク データによって上書きされることになるため、これは悪いことになる可能性があります。
この問題を解決するには、「セレクター」を取り込む非常に気の利いたpublish
オペレーターとマージを組み合わせて使用します。この使用法についてはブログ記事に書きましたが、このテクニックを思い出させてくれた Jedi JW に感謝します。ネットワーク オブザーバブルpublish
、ネットワーク オブザーバブルが発行を開始する時点までディスク キャッシュから発行を開始するセレクターを提供します。ネットワーク オブザーバブルが出力を開始すると、ディスク オブザーバブルからの結果はすべて無視されます。これは完璧で、発生する可能性のある問題に対処します。
以前はmerge
演算子を使用していましたが、「resultAge」を監視することで結果が上書きされる問題を解決しました。この古い実装について知りたい場合は、古いPseudoCacheMergeFragment
の例を参照してください。
これは、特定の間隔でタスクを実行する必要がある多くのケースを処理するために RxJava のtimer
、 interval
、およびdelay
演算子を使用する方法を示す、非常にシンプルでわかりやすい例です。 Android TimerTask
には基本的に「NO」と言います。
ここで紹介するケースは次のとおりです。
このデモの詳細をより適切に説明する付随のブログ投稿があります。
Android で RxJava を使用するときによく寄せられる質問は、「構成変更 (アクティビティのローテーション、言語ロケールの変更など) が発生した場合に、オブザーバブルの作業を再開するにはどうすればよいですか?」というものです。
この例では、1 つの戦略を示します。保持されたフラグメントを使用します。私は、かなり前に Alex Lockwood によるこの素晴らしい投稿を読んでから、保持されたフラグメントを「ワーカー フラグメント」として使用し始めました。
スタートボタンを押して、画面を思う存分回転させてください。オブザーバブルが中断したところから継続していることがわかります。
この例で使用されているソース オブザーバブルの「ホットさ」には、特定の癖があります。詳細については、私のブログ投稿をご覧ください。
その後、別のアプローチを使用してこの例を書き直しました。 ConnectedObservable
アプローチは機能しましたが、トリッキーな場合がある「マルチキャスト」の領域に入ります (スレッド セーフ、.refcount など)。一方、主題ははるかに単純です。ここでSubject
を使用して書き換えられているのを確認できます。
私は主題について考える方法について別のブログ投稿を書き、具体的に説明しました。
Volley は、Google が IO '13 で導入したもう 1 つのネットワーキング ライブラリです。親切な github 市民がこの例を提供してくれたので、Volley と RxJava を統合する方法が分かりました。
ここでは Subject のシンプルな使用法を活用します。正直なところ、すでにObservable
経由でアイテムが配信されていない場合 (Retrofit やネットワーク リクエストなど)、Rx を使用して物事を複雑にする正当な理由はありません。
この例では基本的にページ番号を件名に送信し、件名が項目の追加を処理します。 concatMap
の使用と、 _itemsFromNetworkCall
からのObservable<List>
の戻りに注目してください。
おまけに、 PaginationAutoFragment
例も含めました。これは、ボタンを押すことなく「自動ページ分割」されます。前の例がどのように機能するかを理解していれば、簡単に理解できるはずです。
以下に、その他の派手な実装をいくつか示します (楽しく読んでいましたが、個人的には必要ないと考えたため、実際のアプリでは使用しませんでした)。
以下の ASCII 図は、次の例の意図を堂々と表現しています。 f1、f2、f3、f4、f5 は本質的にネットワーク呼び出しであり、実行されると、将来の計算に必要な結果が返されます。
(flatmap)
f1 ___________________ f3 _______
(flatmap) | (zip)
f2 ___________________ f4 _______| ___________ final output
|
____________ f5 _______|
この例のコードは、インターウェブ内の 1 人の skehlet 氏によってすでに書かれています。コードの要点に進みます。これは Pure Java (6) で書かれているため、前の例を理解していればかなり理解できます。時間が許すか、他の説得力のある例がなくなったら、ここでもう一度フラッシュします。
これは、 .timeout
演算子の使用法を示す簡単な例です。ボタン 1 はタイムアウト制約の前にタスクを完了しますが、ボタン 2 はタイムアウト エラーを強制します。
タイムアウト例外が発生した場合にどのように反応するかを示すカスタム Observable をどのように提供できるかに注目してください。
using
) using
演算子は比較的知られておらず、Google で検索するのが難しいことで知られています。これは、(高価な) リソースをセットアップし、使用し、クリーンな方法で破棄するのに役立つ美しい API です。
このオペレーターの優れた点は、コストがかかる可能性があるリソースを厳密に範囲を絞った方法で使用するメカニズムを提供することです。使用 -> セットアップ、使用、廃棄。 DB 接続 (Realm インスタンスなど)、ソケット接続、スレッド ロックなどを考えてください。
Rx でのマルチキャストは闇の芸術のようなものです。心配せずにそれをやり遂げる方法を知っている人はあまり多くありません。この例では 2 つのサブスクライバ (ボタンの形式) を考慮し、さまざまな時点でサブスクライバを追加/削除し、それらの状況でさまざまなオペレータがどのように動作するかを確認できます。
ソース オブザーバブルはタイマー ( interval
) オブザーバブルであり、これが選択された理由は、マルチキャスト実験がリークするかどうかをテスト/確認できるように、非終了オブザーバブルを意図的に選択するためです。
私も 360|Andev でマルチキャストについて詳しく講演しました。興味と時間があれば、最初にその講演 (特にマルチキャスト オペレーターの順列セグメント) を視聴してから、ここにある例をいじってみることを強くお勧めします。
ここにあるすべての例は、RxJava 2.X を使用するように移行されています。
RxBindings、RxRelays、RxJava-Math などの特定のライブラリはまだ 2.x に移植されていないため、場合によっては David Karnok の Interop ライブラリを使用します。
サンプルが過度に不自然なものではなく、現実世界のユースケースを反映していることを確認するように努めています。 RxJava の使用法を示す同様の有用な例がある場合は、お気軽にプル リクエストを送信してください。
私も RxJava について頭を悩ませているので、上記の例のいずれかを実行するより良い方法があると思われる場合は、その方法を説明するイシューを開いてください。さらに良いのは、プル リクエストを送信することです。
Rx のスレッド処理は厄介な作業です。これを支援するために、このプロジェクトでは分析に YourKit ツールを使用します。
YourKit は、Java アプリケーションの監視とプロファイリングのための革新的でインテリジェントなツールを備えたオープン ソース プロジェクトをサポートします。 YourKit は YourKit Java Profiler の作成者です。
Apache License、バージョン 2.0 (以下「ライセンス」) に基づいてライセンスされています。ライセンスのコピーは次の場所で入手できます。
http://www.apache.org/licenses/LICENSE-2.0
適用される法律で義務付けられている場合または書面による同意がない限り、ライセンスに基づいて配布されるソフトウェアは、明示または黙示を問わず、いかなる種類の保証や条件もなく、「現状のまま」で配布されます。ライセンスに基づく許可と制限を規定する特定の言語については、ライセンスを参照してください。
あなたは、修正、プルリクエスト、新しいサンプルなどの形でのこのリポジトリへのすべての貢献が上記のライセンスに従うことに同意するものとします。