LoginSignup
16
8

More than 5 years have passed since last update.

Lifecycleを使ってAutoDisposableを実現する

Posted at

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

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

16
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
8