Help us understand the problem. What is going on with this article?

Lifecycleを使ってAutoDisposableを実現する

More than 1 year has passed since last update.

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

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

kubode
wantedly
「シゴトでココロオドル」ためのビジネスSNS「Wantedly」の開発・運営をしています。
https://wantedlyinc.com/ja/presentations
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away