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 。 特に、 ViewViewObservable.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に関する彼の広範な知識がなければ、私は長い間立ち往生するでしょう。

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


All Articles