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らしくてより理想的になっている気がしました。