RxAndroidをv1.0.0に更新していたところ、AppObservableの部分がすべてエラーになっており、それを解消するまでに案外手間取ってしまったのでそのメモ
AppObservableとは
ActivityやFragment内部でObservable#subscribe()を呼んだ際、そのActivityやFragmentが終了された以降にもonNextやonCompletedが呼ばれてしまうことがあります。
その結果、画面が破棄された状態で画面更新を行おうとしてしまうことでのエラーや、ObservableがActivityやFragmentへの強参照を持ったままになってしまうことによるメモリリークが発生してしまいます。
それを防ぐためにonPauseなどで明示的にSubscription#unsubscribe()を呼んであげる必要がありました。
そのunsubscribeの処理を勝手にやってくれる仕組みがAppObservableでした。
以下の様な感じにObservableをラップすることで使用できました。
public class HogeActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hoge);
// hogeTask()の返り値がObservable
AppObservable.bindActivity(this, hogeTask()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()))
.subscribe(data -> {
// 処理
});
}
}
RxAndroid v1.0.0での変更
今までRxAndroidのライブラリの中に同梱されていたAppObservableの代わりに、別のライブラリを使用する方法へ置き換えられました。
使う際はbuild.gradleに以下を加えてください。
(バージョンは15/10/04時点での最新バージョンです)
compile 'com.trello:rxlifecycle:0.3.0'
compile 'com.trello:rxlifecycle-components:0.3.0'
クラス名の変更だけでなく、使い方も大幅に変わりました。
先ほどのAppObservableを使用していた例をRxLifecycleの仕様に書き換えてみます。
public class HogeActivity extends RxActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hoge);
// hogeTask()の返り値がObservable
hogeTask()
.compose(bindToLifecycle())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
// 処理
});
}
}
- Activityの継承元を
RxActivityに変更 -
AppObservableで囲う方式から、compose()で処理をつなげる方式に変更
継承元のRxActivityは、もともとのActivityが何のActivityを継承していたかで変わります
例:
・AppCompatActivity->RxAppCompatActivity
・AppFragmentActivity->RxFragmentActivity
Fragementの場合も同様に継承元を変更(RxFragmentなど)することでbindToLifecycle()を使用することができます。
bindToLifecycleメソッドは、対象のObservableのsubscribeメソッドが呼ばれた時のライフサイクルの状態に応じて、自動的に終わりのライフサイクルを決定してくれます。
例:
・onCreateの中でsubscribeが呼ばれた時はonDestoryでunsubscribeされる
・onResumeの中でsubscribeが呼ばれた時はonPauseでunsubscribeされる
unsubscribeされる状態を自分で決定したい場合は、bindToLifecycle()をbindUntilEvent(ActivityEvent.STOP)のように書き換えてください(onStopでunsubscribe)
また、注意点として、自動的にunsubscribeが行われる際には、自動的にonCompletedが呼ばれます。
もし、onCompletedでActivityなどへアクセスするコードを書いている場合は、RxLifecycleを使用するのではなく通常通りSubscriptionを各自でunsubscribeしたほうがいいようです。
In most cases this works out fine (since onCompleted() is not often used). In cases where you do not want onCompleted() called during early termination, then it is suggested that you manually handle the Subscription yourself and call unsubscribe() when appropriate.
https://github.com/trello/RxLifecycle/blob/master/README.md
RxActivityを継承したくない
RxActivity内部で行われている動作は、
- on***から得られたライフサイクルのイベントを
-
Observableに流す
という単純な動作しかしていません。
継承を多段にしたくない・すでにBaseActivity/BaseFragmentを用意しているから、そこにマージさせたい といった場合は、この処理を自前で書くと良いでしょう
// Fragmentであれば、AcitivtyEventではなくFragmentEvent
// メソッド名に含まれるActivityという単語もFragmentに置き換える
private final BehaviorSubject<ActivityEvent> lifecycle = BehaviorSubject.create();
@Override
public final <T> Observable.Transformer<? super T, ? extends T> bindUntilEvent(ActivityEvent event) {
return RxLifecycle.bindUntilActivityEvent(lifecycle, event);
}
public final <T> Observable.Transformer<? super T, ? extends T> bindToLifecycle() {
return RxLifecycle.bindActivity(lifecycle);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycle.onNext(FragmentEvent.CREATE);
}
// Activityであれば、onCreate, onStart, onResume, onPause, onStop, onDestoryで lifecycle.onNext(ActivityEvent.***);を呼ぶ
// Fragmentであれば、onAttach, onCreate, onViewCreated, onStart, onResume, onPause, onStop, onDestroyView, onDestory, onDetachでlifecycle.onNext(FragmentEvent.***);を呼ぶ
自前で用意したことにより、RxActivityやRxFragmentを使用しない場合は
compile 'com.trello:rxlifecycle-components:0.3.0'
こちらの依存関係は削除してしまって問題ありません。
おわりに
AppObservableの時より、composeでつなげていくほうがRxらしくてより理想的になっている気がしました。