Grokai * RxJava、パート1:基本

*翻訳者から:「to grok」という動詞をロシア語に翻訳する方法について考えてきました。 一方で、この単語は「理解する」または「実現する」と翻訳され、他方では、ロバートハインラインの小説「異国での見知らぬ人」(この単語が最初に生まれた)を翻訳するとき、翻訳者はそれをロシア語にしたとどろきます。」 私は小説を読まなかったので、この単語にはロシア語の類似物によって伝えられなかった意味的なニュアンスがあると考えたため、英語の同じトレーシングペーパーを翻訳に使用しました。

RxJavaは、Androidプログラマーの間で議論する最もホットなトピックの1つになりました。 唯一の問題は、このような何かに出会っていない場合、その基本を理解することは非常に難しいことです。 関数型リアクティブプログラミングは、命令型の世界から来た場合には理解するのが非常に困難ですが、対処するやいなや、それがいかにクールであるかを実感できます!
RxJavaについての一般的なアイデアをお伝えします。 この一連の記事の目的は、すべてを最後のコンマまで説明することではなく(これを行うことはほとんどできませんでした)、むしろRxJavaとその仕組みに興味を持つことです。

基本


リアクティブコードの基本的な構成要素は、 ObservablesSubscribers 1Observableはデータソースであり、 Subscriberはコンシューマです。
Observableを介したデータ生成は、常に同じ手順に従って行われますObservableは、一定量のデータを「放出」し( Observableは何も出力しない場合があります)、正常にまたはエラーで作業を完了します。 ObservableSubscriberライブしている各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から渡された行を出力するだけです。 myObservablemySubscriberができたmySubscribersubscribe()メソッドを使用してそれらをリンクできます。

 myObservable.subscribe(mySubscriber); //  "Hello, world!" 

mySubscribermyObservableにサブスクmySubscriberするとmyObservablemyObservablemySubscriber 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!」を変更できたら素晴らしいと思います。

オペレーターの紹介


そして、データ変換を目的としたこのような中間ステップがあります。 その名前は演算子であり、 ObservableSubscriber間でデータ操作に使用できます。 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)); 

これを見てくださいObservableSubscriberは最初と同じように見えます! データを変換する中間ステップをいくつか追加しました。 生成された行に署名を追加するコードを再度追加することもできます。

 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: ObservableSubscriberは何でもできる


あなたの想像力を制限しないでください、あなたが望むものは何でも可能です。
Observableはデータベースクエリであり、 Subscriberはクエリ結果を画面に表示する場合があります。 Observableは、画面をクリックすることでもあります。 Subscriberは、このクリックに対する反応を含む場合があります。 Observableはネットワークから受信したバイトストリームであり、 Subscriberはこのデータをストレージデバイスに書き込むことができます。
これは、ほとんどすべての問題を処理できる汎用フレームワークです。

アイデア#2: ObservableSubscriberは、中間の中間ステップから独立しています


ObservableとそれをSubscriberライブしているSubscriber間に、必要Observableだけmap()呼び出しを挿入できます。 システムは簡単に構成でき、その助けを借りて、データの流れを非常に簡単に制御できます。 演算子が正しい入力/出力データで動作する場合、無限の長さ4の変換のチェーンを書くことができます。

これらの重要なアイデアを一緒に見てみると、大きな可能性のあるシステムが表示されます。 ただし、現在はmap()演算子が1つしかないため、あまり記述しません。 この記事の第2部では、RxJavaを使用するときにすぐに使用できる多数の演算子について検討します。

2番目の部分に移動します。


1 SubscriberObserverインターフェイスを実装するため、後者を「基本的なビルディングブロック」と呼ぶことができますが、 Subscriber.unsubscribe()などの便利なメソッドがいくつか追加されているため、実際にはほとんどの場合Subscriber使用します。
2 RxJavaには「ホット」および「コールド」 Observablesます。 ホットObservableは、誰もサブスクライブしていない場合でも、継続的にデータを生成します。 したがって、コールドObservableは、少なくとも1人のサブスクライバーがいる場合にのみデータを生成します(記事ではcold Observables使用しています)。 RxJavaの学習の初期段階では、この違いはそれほど重要ではありません。
3厳密に言えば、 Observable.just()元のコードの完全な類似物でObservable.just()ませんが、これがなぜそうなるのかについては記事の第3部でのみ説明します。
4よろしい、それほど無限ではありません。ある時点で鉄によって課せられた制限に我慢するからです。

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


All Articles