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
ライフサイクルをどのように考慮しますか? 何度も繰り返し感じられる問題がいくつかあります。
- 構成の変更後にサブスクリプションを再開します。
たとえば、Retrofitを使用してRESTリクエストを作成し、その結果をListView
に表示したいとします。 リクエストの実行中にユーザーが電話を回したらどうなりますか? リクエストの実行を再開する必要がありますが、どのように? -
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に関する彼の広範な知識がなければ、私は長い間立ち往生するでしょう。