RxAndroid v1.0.0からのライフサイクルへのバインド方法

More than 3 years have passed since last update.

RxAndroidをv1.0.0に更新していたところ、AppObservableの部分がすべてエラーになっており、それを解消するまでに案外手間取ってしまったのでそのメモ


AppObservableとは

ActivityFragment内部でObservable#subscribe()を呼んだ際、そのActivityFragmentが終了された以降にもonNextonCompletedが呼ばれてしまうことがあります。

その結果、画面が破棄された状態で画面更新を行おうとしてしまうことでのエラーや、ObservableActivityFragmentへの強参照を持ったままになってしまうことによるメモリリークが発生してしまいます。

それを防ぐためにonPauseなどで明示的にSubscription#unsubscribe()を呼んであげる必要がありました。

そのunsubscribeの処理を勝手にやってくれる仕組みがAppObservableでした。

以下の様な感じにObservableをラップすることで使用できました。


HogeActivity.java

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の代わりに、別のライブラリを使用する方法へ置き換えられました。

https://github.com/trello/RxLifecycle

使う際はbuild.gradleに以下を加えてください。

(バージョンは15/10/04時点での最新バージョンです)

compile 'com.trello:rxlifecycle:0.3.0'

compile 'com.trello:rxlifecycle-components:0.3.0'

クラス名の変更だけでなく、使い方も大幅に変わりました。

先ほどのAppObservableを使用していた例をRxLifecycleの仕様に書き換えてみます。


HogeActivity.java

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 -> {
// 処理
});
}
}



  1. Activityの継承元をRxActivityに変更


  2. AppObservableで囲う方式から、compose()で処理をつなげる方式に変更

継承元のRxActivityは、もともとのActivityが何のActivityを継承していたかで変わります


例:

AppCompatActivity -> RxAppCompatActivity

AppFragmentActivity -> RxFragmentActivity


Fragementの場合も同様に継承元を変更(RxFragmentなど)することでbindToLifecycle()を使用することができます。

bindToLifecycleメソッドは、対象のObservablesubscribeメソッドが呼ばれた時のライフサイクルの状態に応じて、自動的に終わりのライフサイクルを決定してくれます。


例:

onCreateの中でsubscribeが呼ばれた時はonDestoryunsubscribeされる

onResumeの中でsubscribeが呼ばれた時はonPauseunsubscribeされる


unsubscribeされる状態を自分で決定したい場合は、bindToLifecycle()bindUntilEvent(ActivityEvent.STOP)のように書き換えてください(onStopunsubscribe

また、注意点として、自動的に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内部で行われている動作は、


  1. on***から得られたライフサイクルのイベントを


  2. Observableに流す

という単純な動作しかしていません。

継承を多段にしたくない・すでにBaseActivity/BaseFragmentを用意しているから、そこにマージさせたい といった場合は、この処理を自前で書くと良いでしょう


BaseActivity.java

// 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.***);を呼ぶ


自前で用意したことにより、RxActivityRxFragmentを使用しない場合は

compile 'com.trello:rxlifecycle-components:0.3.0'

こちらの依存関係は削除してしまって問題ありません。


おわりに

AppObservableの時より、composeでつなげていくほうがRxらしくてより理想的になっている気がしました。