Support Library v26からAppCompatActivity
とFragment
にLifecycleOwner
が実装されるようになりました。
これにより、RxJavaのDisposable
を自動でdispose()
することができるようになります。
LifecycleObserver
Lifecycle#addObserver(LifecycleObserver)
することでライフサイクルの通知を受けることができるインターフェースです。
LifecycleObserver
自体は何もメソッドを持ちません。
基本的にはLifecycleObserver
を実装したFullLifecycleObserver
かGenericLifecycleObserver
を使うことになります。
FullLifecycleObserver
void onCreate(LifecycleOwner source)
やvoid onStart(LifecycleOwner source)
などライフサイクルに応じたコールバックメソッドを持ちます。
GenericLifecycleObserver
void onStateChanged(LifecycleOwner source, Lifecycle.Event event)
のメソッドのみを持ち、ライフサイクルによってevent
が送られてくる形です。
メソッドを1つだけ持つインターフェースなので、ラムダとして使うことができます。
Lifecycle.asObservable
ライフサイクルイベントをRxJavaのObservable
に変換してみます。
fun Lifecycle.asObservable(): Observable<Lifecycle.Event> =
Observable.create { emitter ->
val observer = object : GenericLifecycleObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (!emitter.isDisposed) {
emitter.onNext(event)
if (event == Lifecycle.Event.ON_DESTROY) {
emitter.onComplete()
removeObserver(this)
}
}
}
}
if (!emitter.isDisposed) addObserver(observer)
}
これにより、ライフサイクルのメソッドをオーバーライドしなくても、所定のタイミングでコールバックを受けることができるようになります。
class FooFragment: Fragment() {
private lateinit var disposable: Disposable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
disposable = lifecycle.asObservable()
.filter { it == Lifecycle.Event.ON_RESUME }
.subscribe { toast("resumed") }
}
override fun onDestroy() {
disposable.dispose()
super.onDestroy()
}
}
AutoDispose
Disposable
をライフサイクルに合わせて自動でdispose
するようにします。
// Fork from LifecycleRegistry.downEvent
val Lifecycle.State.downEvent: Lifecycle.Event
get() = when (this) {
Lifecycle.State.INITIALIZED -> throw IllegalArgumentException()
Lifecycle.State.CREATED -> Lifecycle.Event.ON_DESTROY
Lifecycle.State.STARTED -> Lifecycle.Event.ON_STOP
Lifecycle.State.RESUMED -> Lifecycle.Event.ON_PAUSE
Lifecycle.State.DESTROYED -> throw IllegalArgumentException()
else -> throw IllegalArgumentException("Unexpected state value " + this)
}
fun Disposable.autoDispose(
lifecycle: Lifecycle,
disposeEvent: Lifecycle.Event = lifecycle.currentState.downEvent
) {
val observer = object : GenericLifecycleObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == disposeEvent) {
dispose()
lifecycle.removeObserver(this)
}
}
}
lifecycle.addObserver(observer)
}
これによりprivateなDisposable
プロパティを作って、対応するライフサイクルでdispose
する手間を減らせます。
class FooFragment: Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.asObservable()
.filter { it == Lifecycle.Event.ON_RESUME }
.subscribe { toast("resumed") }
.autoDispose(lifecycle)
}
}
autoDispose(this)
Double context extension pattern (参照)も実装して、もう少し記述を減らしてみます。
val LifecycleOwner.disposeOn: Disposable.(Lifecycle.Event) -> Unit
get() = { disposeEvent -> disposeOn(lifecycle, disposeEvent) }
val LifecycleOwner.autoDispose: Disposable.() -> Unit
get() = { autoDispose(lifecycle) }
これを利用することで、毎回lifecycle
を渡す手間が省けます。
class FooFragment: Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.asObservable()
.filter { it == Lifecycle.Event.ON_RESUME }
.subscribe { toast("resumed") }
.autoDispose()
}
}
Double context extension patternをやってみましたが、IDEが補完してくれないようですね・・
まとめ
個人的にはDisposable
の管理を自動化できるのは良いなと感じました。
RxLifecycleもArchitectureComponentsに対応していたりしますが、bindToLifecycle
をsubscribe
の直前にしないとバグを産んだりするので、今回紹介したアプローチのほうが個人的には扱いやすいのではないかと思いました。
すでにライブラリあるよ!とかもっといい方法があればツッコミお待ちしております。