Android
RxJava

Lifecycleを使ってAutoDisposableを実現する

Support Library v26からAppCompatActivityFragmentLifecycleOwnerが実装されるようになりました。
これにより、RxJavaのDisposableを自動でdispose()することができるようになります。

LifecycleObserver

Lifecycle#addObserver(LifecycleObserver)することでライフサイクルの通知を受けることができるインターフェースです。

LifecycleObserver自体は何もメソッドを持ちません。
基本的にはLifecycleObserverを実装したFullLifecycleObserverGenericLifecycleObserverを使うことになります。

FullLifecycleObserver

void onCreate(LifecycleOwner source)void onStart(LifecycleOwner source)などライフサイクルに応じたコールバックメソッドを持ちます。

GenericLifecycleObserver

void onStateChanged(LifecycleOwner source, Lifecycle.Event event)のメソッドのみを持ち、ライフサイクルによってeventが送られてくる形です。
メソッドを1つだけ持つインターフェースなので、ラムダとして使うことができます。

Lifecycle.asObservable

ライフサイクルイベントをRxJavaのObservableに変換してみます。

ArchitectureComponents.kt
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)
        }

これにより、ライフサイクルのメソッドをオーバーライドしなくても、所定のタイミングでコールバックを受けることができるようになります。

FooFragment.kt
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するようにします。

ArchitectureComponents.kt
// 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する手間を減らせます。

FooFragment.kt
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 (参照)も実装して、もう少し記述を減らしてみます。

ArchitectureComponents.kt
val LifecycleOwner.disposeOn: Disposable.(Lifecycle.Event) -> Unit
    get() = { disposeEvent -> disposeOn(lifecycle, disposeEvent) }

val LifecycleOwner.autoDispose: Disposable.() -> Unit
    get() = { autoDispose(lifecycle) }

これを利用することで、毎回lifecycleを渡す手間が省けます。

FooFragment.kt
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に対応していたりしますが、bindToLifecyclesubscribeの直前にしないとバグを産んだりするので、今回紹介したアプローチのほうが個人的には扱いやすいのではないかと思いました。

すでにライブラリあるよ!とかもっといい方法があればツッコミお待ちしております。