Grokai RxJava、パート4:Jet Android

最初2番目3番目のパートでは、一般的な用語でRxJavaデバイスについて説明しました。 「ファイン、でもAndroidの開発者として、これはどのように役立つのでしょうか?」と思うかもしれません。記事の最後の部分では、実用的な情報を提供します。



Rxandroid



RxAndroidは、Android向けに特別に記述されたRxJava拡張機能で、RxJavaを囲む特別なバインディングが含まれており、作業が楽になります。



まず、 AndroidSchedulers



クラスがあります。これは、Android固有のスレッド用の既製のスケジューラーを提供します。 UIスレッドでコードを実行する必要がありますか? 問題ありませんAndroidSchedulers.mainThread()



使用してください:



 retrofitService.getImage(url) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
      
      





独自のHandler



がある場合は、 HandlerThreadScheduler



1を使用して関連するスケジューラを作成できます。



次に、Android SDKの一部のクラスにライフサイクル機能を提供するAndroidObservable



があります。 bindActivity()()



およびbindFragment()



演算子があります。これらの演算子は、自動的にAndroidSchedulers.mainThread()



を使用して監視するだけでなく、 Activity



またはFragment



が作業を完了し始めたときにデータの生成を停止します(この方法では問題が発生しません。これができなくなったときに状態を変更しようとしています)。



 AndroidObservable.bindActivity(this, retrofitService.getImage(url)) .subscribeOn(Schedulers.io()) .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
      
      





AndroidObservable.fromBroadcast()



も気に入っています。これにより、 BroadcastReceiver



ように機能するObservable



を作成できます。 ここでは、たとえば、ネットワークステータスが変更されたときに通知を受け取ることができます。



 IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); AndroidObservable.fromBroadcast(context, filter) .subscribe(intent -> handleConnectivityChange(intent));
      
      





最後に、 View



バインディングを追加するViewObservable



がありView



。 特に、 View



ViewObservable.text()



されるたびに通知する場合はViewObservable.clicks()



ステートメント、およびTextView



がコンテンツを変更するたびにViewObservable.text()



が含まれます。



 ViewObservable.clicks(mCardNameEditText, false) .subscribe(view -> handleClick(view));
      
      







後付け



Android用の人気のあるRESTクライアントであるRetrofitとして、RxJavaをサポートする注目すべきライブラリがあります。 通常、その中で非同期メソッドを定義するときは、 Callback



を使用します:



 @GET("/user/{id}/photo") void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
      
      





ただし、RxJavaを使用する場合は、代わりに友人のObservable



返すことができます。



 @GET("/user/{id}/photo") Observable<Photo> getUserPhoto(@Path("id") int id);
      
      





その後、必要に応じてすぐにObservable



を使用できますObservable



からデータを取得できるだけでなく、その場で変換することもできます。



Observable



含まれるObservable



サポートにより、複数のRESTリクエストを簡単に組み合わせることができます。 たとえば、2つのAPIメソッドがあり、1つ目は写真を返し、2つ目はそのメタデータを返します。 これらのクエリの結果を一緒に収集できます。



 Observable.zip( service.getUserPhoto(id), service.getPhotoMetadata(id), (photo, metadata) -> createPhotoWithData(photo, metadata)) .subscribe(photoWithData -> showPhoto(photoWithData));
      
      





2番目の部分で似たようなことを示しました( flatMap()



を使用)。 ここで、RxJava + Retrofitバンドルを使用して複数のRESTリクエストを1つにまとめることがいかに簡単かを示したいと思いました。



古い、遅いコード



RetrofitがObservables



を返すことができるという事実は素晴らしいですが、それらについて何も聞いていない別のライブラリがある場合はどうでしょうか。 または、 Observable



動作するように、手間をかけずに変更したい古いコードがありますか。 簡単に言えば、すべてを書き直さずに、古いコードと新しいコードをどのように組み合わせるのでしょうか?



ほとんどの場合、 Observable.just()



およびObservable.from()



を使用するだけで十分です。



 private Object oldMethod() { ... } public Observable<Object> newMethod() { return Observable.just(oldMethod()); }
      
      





oldMethod()



が高速の場合、これはうまく機能しますが、そうでない場合はどうなりますか? oldMethod()



oldMethod()



に呼び出され、その結果のみがObservable.just()



渡されるため、スレッド全体をブロックします。

この問題を回避するには、次のトリックを使用できます(私は常に使用しています): Observable.defer()



で遅いコードをラップします。



 private Object slowBlockingMethod() { ... } public Observable<Object> newMethod() { return Observable.defer(() -> Observable.just(slowBlockingMethod())); }
      
      





これで、受け取ったObservable



は、サブスクライブするまでslowBlockingMethod()



呼び出しません。



ライフサイクル



私は最後に最も難しい部分を残しました。 RxJavaを使用する場合、 Activity



ライフサイクルをどのように考慮しますか? 何度も繰り返し感じられる問題がいくつかあります。



  1. 構成の変更後にサブスクリプションを再開します。

    たとえば、Retrofitを使用してRESTリクエストを作成し、その結果をListView



    に表示したいとします。 リクエストの実行中にユーザーが電話を回したらどうなりますか? リクエストの実行を再開する必要がありますが、どのように?
  2. Context



    への参照を保持するObservables



    によって引き起こされるメモリリーク。

    この問題は、 Context



    へのリンクを何らかの形で保持するサブスクリプションを作成することによって発生します( Views



    場合はそれほど難しくありません!) Observable



    が時間通りに作業を終了しない場合、ある時点で、多くのメモリを解放できます。


残念ながら、ここには特効薬はありませんが、あなたの人生を単純化できるテクニックがいくつかあります。






最初の問題は、RxJavaの組み込みキャッシュメカニズムを使用して処理できます。これにより、同じObservable



サブスクライブ/サブスクライブ解除できます。 具体的には、 cache()



(またはreplay()



)は、購読を解除できた場合でも、以前に実行されたリクエストを継続します。 これは、 Activity



再作成した後も引き続き作業できることを意味します。



 Observable<Photo> request = service.getUserPhoto(id).cache(); Subscription sub = request.subscribe(photo -> handleUserPhoto(photo)); // ... Activity ... sub.unsubscribe(); // ...  Activity  ... request.subscribe(photo -> handleUserPhoto(photo));
      
      





両方のケースで同じキャッシュされたrequest



を使用することに注意してください。 したがって、彼が実行するクエリは1回だけ実行されます。 request



を保存する場所はあなた次第ですが、ライフサイクルに関連するすべての決定と同様に、これはライフサイクルによって生成される変更(保持されたフラグメント、シングルトンなど)を受ける場所でなければなりません。






2番目の問題は、ライフサイクルに従ってサブスクリプションのサブスクリプションを正しく解除することで解決されます。 一般的な解決策は、 CompositeSubscription



を使用してすべてのサブスクリプションを保存し、それらをすべてonDestroy()



またはonDestroy()



onDestroy()



解除することです。



 private CompositeSubscription mCompositeSubscription = new CompositeSubscription(); private void doSomething() { mCompositeSubscription.add( AndroidObservable.bindActivity(this, Observable.just("Hello, World!")) .subscribe(s -> System.out.println(s))); } @Override protected void onDestroy() { super.onDestroy(); mCompositeSubscription.unsubscribe(); }
      
      





生活を簡素化するために、 CompositeSubscription



を含む基本的なActivity



/ Fragment



を作成できます。これにより、後ですべてのサブスクリプションを保存し、自動的にクリアされます。



注意! CompositeSubscription.unsubscribe()



を呼び出すとすぐに、 CompositeSubscription.unsubscribe()



このインスタンスは使用できなくなります(つまり、もちろんサブスクリプションを追加できますが、すぐにunsubscribe()



を呼び出します!) 今後もCompositeSubscription



を引き続き使用する場合は、新しいインスタンスを作成する必要があります。



これらの問題の両方を解決するために、追加のコードを書くことを余儀なくされています。そのため、いつか待ち望んでいた天才が天から降りてきて、これをすべて簡素化する方法を見つけてくれることを本当に願っています。



結論は?



RxJavaは比較的新しい技術であり、Androidの場合はさらに新しいため、これまでのところAndroidの結論はありません。 RxAndroidは活発に開発されており、私たち(Androidプログラマー)はまだ良い方法と悪い方法を見つけようとしています。 RxJava + RxAndroidバンドルの優れた使用の一般に認められた例はまだありません。1年後、ここでお伝えしたヒントのいくつかはかなり風変わりなものと見なされるでしょう。



それまでの間、RxJavaはコードの記述プロセスを単純化するだけでなく、それをもう少し面白くすることがわかりました。 それでも私を信じないなら、いつか会ってビールのグラスでそれについて話しましょう。



Matthias Kayにこの記事の準備に非常に貴重な助けをしてくれたことに改めて感謝し、 RxAndroidをさらにクールにするためにみんなに参加することをお勧めします!






1 AndroidSchedulers.mainThread()



は、 HandlerThreadScheduler



使用します。



ご注意 翻訳者: Artem_zinに 、私が問題を引き起こしたいくつかの場所の翻訳を手伝ってくれたことに感謝します。 彼とRxJavaに関する彼の広範な知識がなければ、私は長い間立ち往生するでしょう。



All Articles