LiveDataとStateFlowの違い

Kenji Abe
6 min readMay 23, 2020

--

Photo by Ussama Azam on Unsplash

Coroutines 1.3.6 から StateFlow が導入されました。これは使い方等がLiveDataと似ているため、どういった違いがあるのか調べてみました。

これを書いてるときはまだ Exprimental なので、今後変更があるかもしれません。今回は1.3.7を使っています。

使い方

まずは StateFlow の簡単な使い方です。

LiveDataと同じように定義してあげます。 MutableStateFlow をprivateにして、 StateFlow として外からは変更不可で公開してあげるのが丁寧ですね。
この例では数値をインクリメントしていくような処理になっています。

この StateFlow の変更通知を受け取る方法です。

変更通知を受け取るには collect を使います。また、 collectsuspend 関数になってるので、 lifecycleScope を使っています。

見ての通り LiveData と使い方は非常に似ています。これらの違いについて分かったことをまとめていきたいと思います。

初期値

MutableLiveData は元々、初期値をコンストラクタ等で設定することはできませんでした。最近になってコンストラクタで初期値を渡すことができるようになりました。
val _liveData = MutableLiveData() のようにパラメータなしコンストラクタが可能です。

逆に MutableStateFlow はコンストラクタに初期値が必要です。もしnullにしたければ nullable な型パラメータで宣言する必要があります。

最初の変更通知

初期値の箇所と関連しますが、 MutableLiveData の場合はコンストラクタに何も渡さなければ、 observe したタイミングでは何も通知されません。

MutableStateFlow の場合は必ずコンストラクタに初期値にパラメータが必要になります。そのため collect したタイミングで初期値が必ず通知されることになります。これはコンストラクタにnullを設定した場合でも通知されます。

NonNull / Nullable

LiveData はJavaで実装されてるため型パラメータをNonNullな型で指定しても、nullを設定することが可能になっています。

StateFlow のほうは実装もKotlinのため型パラメータがNonNullであれば、nullを設定することができず、安全に使用することが可能です。

補足: lifecycle-livedata-core-ktx:2.3.0-alpha03 からNonNullな型パラメータの場合はnullを設定するとlintチェックでエラーになるようになっています。

メインスレッド / バックグラウンドスレッド

MutableLiveData の場合はバックグラウンドスレッドから値を設定するときは value = xxx ではなく、 postValue(xxx) を使う必要がありました。
また、 observe するときはメインスレッドである必要があります。

MutableStateFlow の場合は、メインスレッド・バックグラウンドスレッド関係なく value = xxx を使います。

StateFlow の場合は CoroutineContext によって値を受け取るときのスレッドを制御することができます。

細かい説明は省略しますが、 lifecycleScope はデフォルトではメインスレッドで動くようになっています。これは制御可能で、次のようにすることで collect の処理を IO Workerで動かすことが可能になります。そのため、UI変更の処理をしてるともちろんクラッシュします。

StateFlow は柔軟に制御することが可能となっています。

Lifecycle

LiveData は LifecycleOwner が STARTED 以降の場合に通知され、もしアプリがバックグラウンドにいる場合は通知されません。
(LifecycleOwner を自作したりすることでコントロール可能)

StateFlow は CoroutineContext が有効な間は通知が受け取れるようになっています。自分で Job などで CoroutineContext を作って細かくコントロールすることも可能です。 cancel() を呼び出してそれ以降、通知を受け取れなくすることも可能です。

lifecycleScope を使う場合ですが、 lifecycleScope.launch を使うと CREATED 以降の場合に通知されるので、アプリがバックグラウンドにいっても通知を受け取ります。もし、 LiveData と同じライフサイクルにしたい場合は、 lifecycleScope.launchWhenStarted を使います。

Operator

LiveData と違って StateFlow はベースが Flow なので、強力なオペレーターが用意されています。

良い例が思い浮かばなかったですが、これ上の例以外にもいくつかオペレーターが用意されています。

複数Observer

LiveData は複数 observe した場合もそれぞれ通知が届きます。

StateFlow は特殊です。同じ CoroutineContext 内で何回 collect しても通知が届くのは最初に collect した箇所のみです。

また、別の CoroutineContext で collect した場合は、それぞれに通知が届きます。

DataBinding

説明する必要もなさそうですが一応。 StateFlow は DataBinding には対応していません。

まとめ

LiveDataStateFlow 、似たような構文ですが、色々違う点があります。

LiveData を単純に置き換えるには違う点がいくつかあるので、注意が必要だと思います。また、逆に無理に置き換える必要は特にない印象です。

StateFlow はベースが Flow なので、 asLiveData()LiveData に変換可能ではあります。(これはこれで気をつけることがありますが)

特徴をしっかり理解して、使っていきたいところです。

おまけ

次のような拡張関数を作っておくと、 LiveData と同じような使い方ができます。

--

--

Kenji Abe
Kenji Abe

Written by Kenji Abe

Programmer / Gamer / Google Developers Expert for Android, Kotlin / @STAR_ZERO

No responses yet