Posted at

【Android】LiveDataが使いやすくなる拡張関数【Kotlin】

More than 1 year has passed since last update.

ViewのデータにLiveDataを用いている場合に、Viewのデータを更新するタイミングなどを細かく指定したい場合などがあります。


「Fragmentの生成時にデータがあればそのまま表示するが、データがない場合はローディングを表示し、データをフェッチしてからデータを表示する。ただし、画面回転やバックグラウンドから戻ってきたときなどには、Fragmentのビューを再生成せず、前回と同じ状態を保ちたい。」みたいな


そんな要件をみたすためにLiveDataからのデータの受け取りを工夫し、それらを拡張関数として作成したのでここで紹介します。


データがNullのときは受け取らない

fun <T> LiveData<T>.observeNotNull(owner: LifecycleOwner, observe: (value: T) -> Unit) = apply {

observe(owner, Observer { value ->
value ?: return@Observer
observe(value)
})
}

使用前は以下のようにNullのときは早期returnさせたりしていました。

livedata.observe(this, Observer {

it ?: return@Observer
action(it)
})

これが以下のようにシンプルにかけます。

livedata.observeNotNull(this) { action(it) }


データがNullのときも受け取る

fun <T> LiveData<T>.observeNullable(owner: LifecycleOwner, observe: (value: T?) -> Unit) = apply {

observe(owner, Observer { value ->
observe(value)
})
}

こちらは今までと使い方は変わりませんが、Nullも流れてくるんだなっていうのがわかりやすいです。

livedata.observeNullable(this) { nullableAction(value) }


データを一度だけ受け取る

fun <T> LiveData<T>.observeAtOnce(owner: LifecycleOwner, observe: (value: T) -> Unit) = apply {

observe(owner, object : Observer<T> {
override fun onChanged(value: T?) {
removeObserver(this)
observeNotNull(owner, observe)
}
})
}

observeしてから初めてデータが流れてきた場合のみ受け取れるようになります。observeしたいタイミングではまだLiveDataにデータがないが、そのうちデータが流れてくるので、そのときに一度だけ処理したい場合などに有効です。

livedata.observeAtOnce(this) { action(it) }

もしデータがすでにあり、一度だけデータを受け取りたい場合は、action(livedata.value) のように直接使うと良いと思います。


データがないときに一度だけ別のアクションをする

fun <T> LiveData<T>.doIfEmpty(doing: () -> Unit) = apply {

value ?: doing()
}

LiveDataにデータがない場合に何かを実行する。他の拡張関数と組み合わせて使うことが多い。

livedata

.doIfEmpty { initialize() }
.observeNotNull(this) { action(it) }


データがあるときに一度だけ別のアクションをする

fun <T> LiveData<T>.doIfNotEmpty(doing: (value: T) -> Unit) = apply {

value?.let { doing(it) }
}

LiveDataにデータがある場合に何かを実行する。こちらも他の拡張関数と組み合わせて使うことが多い。observeAtOnce との違いは、受け取りを待たずに即時実行されることです。

livedata

.doIfEmpty { initialize() }
.doIfNotEmpty { action(it) }

こちらは他の observe 系と組み合わせるとobserve時に、2回受け取り処理をすることになるので気をつけたい。処理タイミングを細かく指定したい場合には良いかも。

livedata

.doIfNotEmpty { action(it) }
.observeNotNull(this) { anotherAction(it) }


補足

LiveDataからのデータの受け取りタイミングをできるだけ正確に管理したい場合は、ActivityやFragmentでの受け取りタイミングがどこで発生するのかと、そのときにLiveDataを持っているViewModelがどのライフサイクルで動いているものかなど意識する必要があります。

LiveDataをFlowableから生成している場合は、FlowableがBehaviorなのかPublishなのかも関係してきそうです。次回そこらへんも含めて図を使って説明します。