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よろしい、それほど無限ではありません。ある時点で鉄によって課せられた制限に我慢するからです。