新しく追加されたAACのLifecycleObserverを使ってAndroidのライフサイクルのコードをスッキリさせよう!

Android Architecture Components(以下AAC)とはあんまり関係ないのですが、AACのライブラリのなかに入っているものでAACの中に設計として使う以外にも使えそうだなーと思ったLifecycleObserverを使ってコードスッキリクッキングをしようとおもいます。

LifecycleObserverについて

https://developer.android.com/reference/android/arch/lifecycle/Lifecycle.html
特に詳しいわけでもないのですが、LifecycleObserverActivityFragmentのライフサイクルで呼ばれるonCreate()などのコールバックメソッドが呼ばれたときにそれら(ActivityFragment)の実装と切り離してライフサイクルを扱えるようにしてくれたものです(たぶん)。LifecyclerOwnerと対になるというものみたいです。LifecyclerOwnerLifecycleObserverの関係はこんな感じです。

Kotlin
val myLifecyclerObserver = MyLifecycleObserver()
lifecyclerOwner.lifecycle.addObserver(myLifecyclerObserver)

典型的なオブザーバーパターンですね(ええ、たぶん)。
きっとLifecycle側がMyLifecycleObserverに各ライフサイクルイベントで通知してくれるんでしょうねー。
わかりやすいインターフェイスデザインで助かります。

何をスッキリさせたいか

これについて多分割とたくさんの人に動機があると思うんですが、ActivityFragmentのライフサイクルのコールバックって見づらいですよね。
僕個人は特にonStart()/onCreate()onStop()/onDestroy()をコード上になるべく近くに配置したいです。保守性って意味で。
でも簡単に割とバラバラになるし、ほかのメソッドと合わせて順番なんてその時の事情で様々です。
BroadcastReceiverなんかは僕が一番嫌いなコードになりやすいです。メンバー変数定義して、その変数の複数のコールバックで必要になり。
これまたよくやるんですが、unregisterしわすれたりなんてこともあってうっとうしいですね。

つらみコード?
class MainActivity: AppCompatActivity() {
    val broadCastReceiver = object : BroadcastReceiver() {
        override fun onReceive(contxt: Context?, intent: Intent?) {
            when (intent?.action) {
                BROADCAST_DEFAULT_ALBUM_CHANGED -> handleAlbumChanged()
                BROADCAST_CHANGE_TYPE_CHANGED -> handleChangeTypeChanged()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, MyFragment.newInstance(), "MyFragment")
                .commit()
    }

    override fun onStart() {
        super.onStart()
        // うっかりわすれるかも
        registerReceiver(broadCastReceiver, IntentFilter(BROADCAST_DEFAULT_ALBUM_CHANGED))
    }

    override fun onStop() {
        super.onStop()
        // うっかりわすれる事多し!(という前提 
        unregisterReceiver(broadCastReceiver)
    }
}

ミスをさそいやすい設計だし、コードは見にくくなる。嫌がらせのようなコードですよ。

BroadcastReceiverを扱うコードをLifecycleObserverでスッキリさせる

長年、僕がやりたかったことがついに?できるようになりました。長かった。
これが僕の理想形です!

MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, MyFragment.newInstance(), "MyFragment")
                .commit()

        initBroadcastReceiver()

    }

    fun initBroadcastReceiver(): BroadcastReceiver {
        return object : BroadcastReceiver() {
            override fun onReceive(contxt: Context?, intent: Intent?) {
                when (intent?.action) {
                    BROADCAST_DEFAULT_ALBUM_CHANGED -> handleAlbumChanged()
                    BROADCAST_CHANGE_TYPE_CHANGED -> handleChangeTypeChanged()
                }
            }
        }.apply {
            this@MainActivity.lifecycle.addObserver(
                    DoOnEachLifecycleObserver()
                            // ここに同時にかける!
                            .doOnStart { registerReceiver(this, IntentFilter(BROADCAST_DEFAULT_ALBUM_CHANGED)) }
                            .doOnStop  { unregisterReceiver(this) }
            )
        }
    }
}

どうです?スッキリしました?
スッキリあんまりしてないですよね!(汗
だけどみてください。register/unregisterのコードが一か所に並べられています!こんな風に書けたら誰も最初から失敗なんてしないっての!って感じですよ。

スッキリ?させるためにどうやったか?

では種明かしです。
まぁそんなに難しいことはしてないです。

DoOnEachLifecycleObserver.kt
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent

class DoOnEachLifecycleObserver : LifecycleObserver {
    private var onCreateRunner: DoOnEachRunner? = null
    private var onResumeRunner: DoOnEachRunner? = null
    private var onStartRunner: DoOnEachRunner? = null
    private var onPauseRunner: DoOnEachRunner? = null
    private var onStopRunner: DoOnEachRunner? = null
    private var onDestroyRunner: DoOnEachRunner? = null

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun onCreated() = onCreateRunner?.run()
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStarted() = onStartRunner?.run()
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResumed() = onResumeRunner?.run()
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPaused() = onPauseRunner?.run()
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStopped() = onStopRunner?.run()
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroyed() = onDestroyRunner?.run()

    public fun doOnCreatet(run: () -> Unit): DoOnEachLifecycleObserver {
        onCreateRunner = DoOnEachRunner(run)
        return this
    }

    public fun doOnStart(run: () -> Unit): DoOnEachLifecycleObserver {
        onStartRunner = DoOnEachRunner(run)
        return this
    }

    public fun doOnResume(run: () -> Unit): DoOnEachLifecycleObserver {
        onResumeRunner = DoOnEachRunner(run)
        return this
    }

    public fun doOnPause(run: () -> Unit): DoOnEachLifecycleObserver {
        onPauseRunner = DoOnEachRunner(run)
        return this
    }
    public fun doOnStop(run: () -> Unit): DoOnEachLifecycleObserver {
        onStopRunner = DoOnEachRunner(run)
        return this
    }
    public fun doOnDestroy(run: () -> Unit): DoOnEachLifecycleObserver {
        onDestroyRunner = DoOnEachRunner(run)
        return this
    }
    inner class DoOnEachRunner(private val runner: () -> Unit) {
        public fun run() = runner.invoke()
    }
}

このクラスを用意してあげましょう。
たったこれだけです。

おまけ

RxJavaを使っている人のために簡単なおまけも用意しておきました。意外と便利なんじゃないですかね?

SimpleDisposableRxLifecycleObserver.kt
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable

class SimpleDisposableRxLifecycleObserver : LifecycleObserver {
    private val disposables: CompositeDisposable = CompositeDisposable()
    fun addAll(vararg disposable: Disposable) = disposables.addAll(*disposable)
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStopped() =  { if (!disposables.isDisposed) disposables.dispose() }
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroyed() =  { if (!disposables.isDisposed) disposables.dispose() }
}

これを使うとこんな感じに書けます。

lifecycle.addObserver(SimpleDisposableRxLifecycleObserver ().apply {
    this.addAll(
            // 説明不要な一時変数は極力作らない
            outputs.duration.subscribe(img_pulse.pulse()),
            outputs.bpmText.subscribe(text_bpm.text())
    )
})       

そしてActivityFragment側でdispose()を呼ばなくてよくなります。まぁ普段からやっていることをやらなくなるのは気持ち悪いって人は無理でしょうが、なるべくコードをきれいにしたい人には良いかもしれませんね。

まとめ

さてどうだったでしょうか?
LifecycleObserverActivityFragmentからライフサイクルの処理を一部切り出すものだということがわかりました。
AACでアーキテクチャを構成するためだけじゃなくて、それ以外にも使えそうだということがわかりました。
これを使うと今まで面倒だったコードが少しはマシになる可能性も出てきました。
ですが、やはりこのコードは今までのコードの書き方とは異なるので気持ち悪いかもしれません。
しかし既存のコードの書き方が常に良いとは限らないと思うので、何が良いか常に考えていければよいかもしれませんね。
今日はスッキリしないコードに対する一つの提案をしてみました。

ここまで読んでくださりありがとうございました。

個人的な感想を言うと、もっとLifecycleObserverの実装についていろいろ知りたいです。
書いていて、あー実際の挙動についてもっと知っておかないとだめだなーと思いました。
おいおい調査して内容に補完ができればと思います。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.