RxJavaは、Androidプログラマーの間で議論する最もホットなトピックの1つになりました。 唯一の問題は、このような何かに出会っていない場合、その基本を理解することは非常に難しいことです。 関数型リアクティブプログラミングは、命令型の世界から来た場合には理解するのが非常に困難ですが、対処するやいなや、それがいかにクールであるかを実感できます!
RxJavaについての一般的なアイデアをお伝えします。 この一連の記事の目的は、すべてを最後のコンマまで説明することではなく(これを行うことはほとんどできませんでした)、むしろRxJavaとその仕組みに興味を持つことです。
基本
リアクティブコードの基本的な構成要素は、
Observables
と
Subscribers
1 。
Observable
はデータソースであり、
Subscriber
はコンシューマです。
Observable
を介したデータ生成は、常に同じ手順に従って行われます
Observable
は、一定量のデータを「放出」し(
Observable
は何も出力しない場合があります)、正常にまたはエラーで作業を完了します。
Observable
に
Subscriber
ライブしている各
Subscriber
について、
Subscriber.onNext()
メソッドが各データストリーム要素に対して呼び出され、その後
Subscriber.onComplete()
と
Subscriber.onError()
両方を呼び出すことができます。
これはすべて、通常の「オブザーバー」パターンと非常に似ていますが、1つの重要な違いがあります。
Observables
は、誰かが明示的にサブスクライブするまでデータの生成を開始しないことが多い2 言い換えると、木が倒れても近くに誰もいない場合、倒れた音は聞こえません 。
こんにちは世界!
小さな例を取り上げましょう。 最初に、単純な
Observable
作成します。
Observable<String> myObservable = Observable.create( new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> sub) { sub.onNext("Hello, world!"); sub.onCompleted(); } } );
Observable
は文字列「Hello、world!」を生成して終了します。 次に、データを受信してそれを使用するために
Subscriber
を作成します。
Subscriber<String> mySubscriber = new Subscriber<String>() { @Override public void onNext(String s) { System.out.println(s); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { } };
Subscriber
は、
Observable
から渡された行を出力するだけです。
myObservable
と
mySubscriber
ができた
mySubscriber
、
subscribe()
メソッドを使用してそれらをリンクできます。
myObservable.subscribe(mySubscriber); // "Hello, world!"
mySubscriber
を
myObservable
にサブスク
mySubscriber
すると
myObservable
、
myObservable
は
mySubscriber
onNext()
および
onComplete()
メソッドを
mySubscriber
、その結果、
mySubscriber
は「Hello、world!」をコンソールに出力し、実行を終了します。
コードを簡素化する
一般的に言って、「Hello、world!」をコンソールに出力するような単純なタスクには、あまりにも多くのコードを記述しました。 何が何であるかを簡単に理解できるように、このコードを特別に作成しました。 RxJavaには、この問題を解決するためのより多くの合理化された方法があります。
まず、
Observable
単純化しましょう。 RxJavaには、最も一般的なタスクの解決に適した
Observable
を作成するためのメソッドがあります。 この例では、
Observable.just()
は1つのデータ要素を生成し、最初のオプション3と同様に実行を完了します。
Observable<String> myObservable = Observable.just("Hello, world!");
次に、
Subscriber
単純化します。
onCompleted()
および
onError()
メソッドには関心がないため、別の基本クラスを使用して、
onNext()
で
onNext()
する必要があるものを決定できます。
Action1<String> onNextAction = new Action1<String>() { @Override public void call(String s) { System.out.println(s); } };
Action
は、
Subscriber
任意の部分を置き換えるために使用できます
onNext()
、
onNext()
、
onError()
、および
onCompete()
代わりに実行される1つ、2つ、または3つの
Action
パラメーターを受け入れることができます。 つまり、
Subscriber
ように置き換えることができます。
myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);
ただし、
onError()
および
onCompete()
は必要ないため、コードをさらに簡素化できます。
myObservable.subscribe(onNextAction); // "Hello, world!"
それでは、チェーンメソッド呼び出しに頼って変数を削除しましょう。
Observable.just("Hello, world!") .subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(s); } });
最後に、Java 8のラムダを使用して、コードをさらに簡素化できます。
Observable.just("Hello, world!") .subscribe(s -> System.out.println(s));
Androidで記述している(したがってJava 8を使用できない)場合は、 retrolambdaを強くお勧めします 。これは、一部の場所で非常に冗長なコードを簡素化するのに役立ちます。
変換
新しいことを試してみましょう。
たとえば、コンソールに表示される「Hello、world!」に署名を追加します。 どうやってやるの? まず、
Observable
を変更できます。
Observable.just("Hello, world! -Dan") .subscribe(s -> System.out.println(s));
これは、
Observable
が定義されているソースコードにアクセスできる場合に機能する
Observable
がありますが、たとえば、誰かのライブラリを使用する場合など、常にそうなるとは限りません。 別の問題:
Observable
を多くの場所で使用しているが、 場合によってのみ署名を追加したい場合はどうでしょうか?
Subscriber
書き換えを試すことができます。
Observable.just("Hello, world!") .subscribe(s -> System.out.println(s + " -Dan"));
このオプションも不適切ですが、他の理由によります。メインスレッドで実行できるため、サブスクライバをできるだけ軽量にしたいのです。 より概念的なレベルでは、サブスクライバーは、受信したデータを変更するのではなく、 応答する必要があります。
中間段階で「Hello、world!」を変更できたら素晴らしいと思います。
オペレーターの紹介
そして、データ変換を目的としたこのような中間ステップがあります。 その名前は演算子であり、
Observable
と
Subscriber
間でデータ操作に使用できます。 RxJavaには多くの演算子があります。そのため、最初は少数の演算子のみに焦点を当てる方が良いでしょう。
特定の状況では、
map()
演算子が最適です。これにより、1つのデータ要素を別のデータ要素に変換できます。
Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { return s + " -Dan"; } }) .subscribe(s -> System.out.println(s));
そして再び、ラムダに頼ることができます:
Observable.just("Hello, world!") .map(s -> s + " -Dan") .subscribe(s -> System.out.println(s));
かっこいい? 大まかに言えば、
map()
演算子は
Observable
であり、データの要素を変換します。
Subscriber
タスクを容易にするために、データに最も便利でシンプルな形式を与えるために必要と考えられる数の
map()
チェーンを作成できます。
地図についてもう1つ()
map()
の興味深い特性は、元の
Observable
と同じ型のデータを生成する必要がないことです。
Subscriber
が生成されたテキストではなく、そのハッシュを表示する必要があるとしましょう:
Observable.just("Hello, world!") .map(new Func1<String, Integer>() { @Override public Integer call(String s) { return s.hashCode(); } }) .subscribe(i -> System.out.println(Integer.toString(i)));
おもしろい:行から始めて、
Subscriber
は整数を受け入れます。 ところで、ラムダについては忘れていました。
Observable.just("Hello, world!") .map(s -> s.hashCode()) .subscribe(i -> System.out.println(Integer.toString(i)));
先ほど言ったように、
Subscriber
できる限り少ない作業
Subscriber
してもらいたいので、別の
map()
を使用してハッシュを
String
戻します:
Observable.just("Hello, world!") .map(s -> s.hashCode()) .map(i -> Integer.toString(i)) .subscribe(s -> System.out.println(s));
これを見てください
Observable
と
Subscriber
は最初と同じように見えます! データを変換する中間ステップをいくつか追加しました。 生成された行に署名を追加するコードを再度追加することもできます。
Observable.just("Hello, world!") .map(s -> s + " -Dan") .map(s -> s.hashCode()) .map(i -> Integer.toString(i)) .subscribe(s -> System.out.println(s));
それでは、次は何ですか?
「よく、いつものように、彼らは簡単な例を示しており、2行のコードでこの問題を解決できるので、この技術はクールだと言っています。」 同意する、例は本当に簡単です。 しかし、そこからいくつかの有用なアイデアを引き出すことができます。
アイデア#1: Observable
でSubscriber
は何でもできる
あなたの想像力を制限しないでください、あなたが望むものは何でも可能です。
Observable
はデータベースクエリであり、
Subscriber
はクエリ結果を画面に表示する場合があります。
Observable
は、画面をクリックすることでもあります。
Subscriber
は、このクリックに対する反応を含む場合があります。
Observable
はネットワークから受信したバイトストリームであり、
Subscriber
はこのデータをストレージデバイスに書き込むことができます。
これは、ほとんどすべての問題を処理できる汎用フレームワークです。
アイデア#2: Observable
とSubscriber
は、中間の中間ステップから独立しています
Observable
とそれを
Subscriber
ライブしている
Subscriber
間に、必要
Observable
だけ
map()
呼び出しを挿入できます。 システムは簡単に構成でき、その助けを借りて、データの流れを非常に簡単に制御できます。 演算子が正しい入力/出力データで動作する場合、無限の長さ4の変換のチェーンを書くことができます。
これらの重要なアイデアを一緒に見てみると、大きな可能性のあるシステムが表示されます。 ただし、現在は
map()
演算子が1つしかないため、あまり記述しません。 この記事の第2部では、RxJavaを使用するときにすぐに使用できる多数の演算子について検討します。
2番目の部分に移動します。
1
Subscriber
は
Observer
インターフェイスを実装するため、後者を「基本的なビルディングブロック」と呼ぶことができますが、
Subscriber.unsubscribe()
などの便利なメソッドがいくつか追加されているため、実際にはほとんどの場合
Subscriber
使用します。
2 RxJavaには「ホット」および「コールド」
Observables
ます。 ホット
Observable
は、誰もサブスクライブしていない場合でも、継続的にデータを生成します。 したがって、コールド
Observable
は、少なくとも1人のサブスクライバーがいる場合にのみデータを生成します(記事ではcold
Observables
使用しています)。 RxJavaの学習の初期段階では、この違いはそれほど重要ではありません。
3厳密に言えば、
Observable.just()
元のコードの完全な類似物で
Observable.just()
ませんが、これがなぜそうなるのかについては記事の第3部でのみ説明します。
4よろしい、それほど無限ではありません。ある時点で鉄によって課せられた制限に我慢するからです。