Android Architecture Components(以下AAC)とはあんまり関係ないのですが、AACのライブラリのなかに入っているものでAACの中に設計として使う以外にも使えそうだなーと思ったLifecycleObserver
を使ってコードスッキリクッキングをしようとおもいます。
LifecycleObserverについて
https://developer.android.com/reference/android/arch/lifecycle/Lifecycle.html
特に詳しいわけでもないのですが、LifecycleObserver
はActivity
やFragment
のライフサイクルで呼ばれるonCreate()
などのコールバックメソッドが呼ばれたときにそれら(Activity
やFragment
)の実装と切り離してライフサイクルを扱えるようにしてくれたものです(たぶん)。LifecyclerOwner
と対になるというものみたいです。LifecyclerOwner
とLifecycleObserver
の関係はこんな感じです。
val myLifecyclerObserver = MyLifecycleObserver()
lifecyclerOwner.lifecycle.addObserver(myLifecyclerObserver)
典型的なオブザーバーパターンですね(ええ、たぶん)。
きっとLifecycle
側がMyLifecycleObserver
に各ライフサイクルイベントで通知してくれるんでしょうねー。
わかりやすいインターフェイスデザインで助かります。
何をスッキリさせたいか
これについて多分割とたくさんの人に動機があると思うんですが、Activity
やFragment
のライフサイクルのコールバックって見づらいですよね。
僕個人は特に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でスッキリさせる
長年、僕がやりたかったことがついに?できるようになりました。長かった。
これが僕の理想形です!
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()
}
// [UPDATE] 2019年1月、一度自分のプロジェクトで使ってみて見直し、
// 追加した拡張関数をつかったもの。これでスッキリが増しました!!
fun initBroadcastReceiver() {
val receiver = broadcastReceiver { _, intent ->
when (intent?.action) {
BROADCAST_DEFAULT_ALBUM_CHANGED -> handleAlbumChanged()
BROADCAST_CHANGE_TYPE_CHANGED -> handleChangeTypeChanged()
}
}
// register/unregisterがここに同時にかける!
this@MainActivity.lifecycle.addObserver {
doOnStart { registerReceiver(receiver, IntentFilter(BROADCAST_DEFAULT_ALBUM_CHANGED)) }
doOnStop { unregisterReceiver(receiver) }
}
}
}
どうです?スッキリしましたね!!
register/unregisterのコードが一か所に並べられています!こんな風に書けたら誰も最初から失敗なんてしないっての!って感じですよ。
スッキリ?させるためにどうやったか?
では種明かしです。
まぁそんなに難しいことはしてないです。
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent
// [NEW] 2019年1月追加した拡張関数
fun Lifecycle.addObserver(f: DoOnEachLifecycleObserver.() -> Unit) =
addObserver(DoOnEachLifecycleObserver().apply(f))
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()
fun doOnCreatet(run: () -> Unit): DoOnEachLifecycleObserver {
onCreateRunner = DoOnEachRunner(run)
return this
}
fun doOnStart(run: () -> Unit): DoOnEachLifecycleObserver {
onStartRunner = DoOnEachRunner(run)
return this
}
fun doOnResume(run: () -> Unit): DoOnEachLifecycleObserver {
onResumeRunner = DoOnEachRunner(run)
return this
}
fun doOnPause(run: () -> Unit): DoOnEachLifecycleObserver {
onPauseRunner = DoOnEachRunner(run)
return this
}
fun doOnStop(run: () -> Unit): DoOnEachLifecycleObserver {
onStopRunner = DoOnEachRunner(run)
return this
}
fun doOnDestroy(run: () -> Unit): DoOnEachLifecycleObserver {
onDestroyRunner = DoOnEachRunner(run)
return this
}
inner class DoOnEachRunner(private val runner: () -> Unit) {
fun run() = runner.invoke()
}
}
このクラスを用意してあげましょう。
同じ要領でBroadcastReceiverもつけちゃいましょう!(2019年1月追記)
// [NEW] 2019年1月追加したBroadcastReceiverを手早く作るやつ
fun broadcastReceiver(f: DoOnReceiveBroadcastReceiver.(Context?, Intent?) -> Unit): BroadcastReceiver =
DoOnReceiveBroadcastReceiver().apply {
doOnReceive { context, intent -> this.f(context, intent) }
}
class DoOnReceiveBroadcastReceiver : BroadcastReceiver() {
private var onReceiveRunner: DoOnReceiveRunner? = null
override fun onReceive(context: Context?, intent: Intent?) {
onReceiveRunner?.run(context, intent)
}
inner class DoOnReceiveRunner(private val runner: (Context?, Intent?) -> Unit) {
fun run(ctx: Context?, intent: Intent?) = runner.invoke(ctx, intent)
}
fun doOnReceive(run: (Context?, Intent?) -> Unit) {
onReceiveRunner = DoOnReceiveRunner(run)
}
}
これもあると良い感じです。
たったこれだけです。
おまけ
RxJavaを使っている人のために簡単なおまけも用意しておきました。意外と便利なんじゃないですかね?
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())
)
})
そしてActivity
やFragment
側でdispose()
を呼ばなくてよくなります。まぁ普段からやっていることをやらなくなるのは気持ち悪いって人は無理でしょうが、なるべくコードをきれいにしたい人には良いかもしれませんね。
まとめ
さてどうだったでしょうか?
LifecycleObserver
はActivity
やFragment
からライフサイクルの処理を一部切り出すものだということがわかりました。
AACでアーキテクチャを構成するためだけじゃなくて、それ以外にも使えそうだということがわかりました。
これを使うと今まで面倒だったコードが少しはマシになる可能性も出てきました。
ですが、やはりこのコードは今までのコードの書き方とは異なるので気持ち悪いかもしれません。
しかし既存のコードの書き方が常に良いとは限らないと思うので、何が良いか常に考えていければよいかもしれませんね。
今日はスッキリしないコードに対する一つの提案をしてみました。
ここまで読んでくださりありがとうございました。
個人的な感想を言うと、もっとLifecycleObserver
の実装についていろいろ知りたいです。
書いていて、あー実際の挙動についてもっと知っておかないとだめだなーと思いました。
おいおい調査して内容に補完ができればと思います。