🐷

kotlinのFlowについて自分なりにまとめ

2023/06/05に公開

この記事について

Flowの説明をもっと簡単にできないかなとまとめたものです
プログラムよりFlowの仕組み等を説明してます
kotlin初心者の自分なりにまとめたもので足りない部分があるかもしれませんがご了承ください

読者対象者

これらを知ってると理解が早くなるかもしれません

  • Coroutines
  • LiveData
  • MVVM

Flowについて

単純にいうと非同期で複数の返り値がくるsuspend関数みたいなもの。同時に複数の場所で値を観測することができる。Coroutinesの仲間。

チャットアプリの例でいうと、サーバーからのチャット更新通知が来たときに各更新処理を行うなど(チャット更新・キャッシュ保存 etc...)
以下にそれっぽいプログラム

// チャット更新通知リスナー的なもの
val chatsListener = ChatsListener()

//チャットFlow
val latestChats: Flow<List<Chat>> = flow {
    chatsListener.updateChat { chats -> 
	//チャット更新時通知を送信
	emit(chats)
    }
}

...

//ViewModel層で観測
latestChats.collect { chats -> 
    // ソート処理など
}

...
//View層で観測
latestChats.collect { chats ->
   // UI更新処理など
}

ちなみにFlowの由来は水の流れをイメージしたもので、上から流れてくる情報を下へと伝播する様子かららしい

Flowの種類

主に使うFlowは以下のような種類がある

  • Flow
  • SharedFlow
  • StateFlow

StateFlowSharedFlowを扱いやすくした機能制限版と思ってok(詳細は公式

大きな違いとしてはFlow更新後の値を持たず(観測場所への更新通知だけ)SharedFlowStateFlow更新後の値を持つ
そのためShareFlow, StateFlowLiveDataに近い(実際に値の更新・取得はvalueで行える)

LiveDataとの比較

StateFlow系は非同期処理やDataBindingで使われるLiveDataのほぼ上位互換
LiveDataで不便だった部分がいろいろ直っているので、基本的にLiveDataはStateFlowに置き換えるのが良い
参考URL: livedataとstateflowの違い

自分的大きな利点は

  • NullableだったLiveDataと違い、NonNullで値が観測できる
    初期値を設定したLiveDataでもNullチェックしなければいけなかったのでとても楽になった
  • オペレーターが多種(map,filterなど)
    非同期に受信したデータをfilter等できる関数が揃ってるのでコードが書きやすい
    個人的にはVueのcomputed(他の変更に伴って再計算される変数) みたいな使い方ができるから楽(非同期で変更される複数の情報を組み合わせるなど)
    以下プログラム例
Vue
...

// ユーザーリスト
const users = ref<Users[]>([])

// ソート済みユーザーリスト
const sortedUsers = computed(()=>{
	// (ちなみにこれは最近追加されたTS新ソート関数)
        users.toSorted() 
    })

...
ViewModel
...

// ユーザーリスト
private val _usersStateFlow = MutableStateFlow(ArrayList<User>())
val usersStateFlow = _usersStateFlow.asStateFlow()

// ソート済みユーザーリスト(VueのComputedもどき)
val sortedUsersStateFlow = usersStateFlow.transform{
    // ユーザーリスト更新時にソートして通知
    emit(it.sortedBy { user -> user.age}) 
}.stateIn(
    // 観測のスコープを設定する(呪文だと思ってよし)
    // 詳細:https://android.benigumo.com/20210827/mvvm-kotlin-flow/
    viewModelScope, 
    SharingStarted.WhileSubscribed(5000)
    listOf()
)

...

ちなみにcombine()を使うと複数のFlowの変更通知を受け取った処理もできるので、比較関数変更orユーザーリスト変更時に自動でソートすることもできる
LiveDataだとMediatorLiveDataを使って変更時に更新する手もあるが、変数定義の形で書けてわかりやすいのがとても良い

DataBindingでもLiveDataの代用としてStateFlowが利用でき、asLiveData()LiveDataを取得することも可能である(Flow側更新時にもちゃんと更新通知される)

さいご

Flowは名前の通り流れるようにかなり柔軟な非同期処理を行えるので活用できるととても便利
基本的にLiveDataStateFlowで代用しましょう(LiveDataでできないことがあるというほどではないので、すでにLiveDataだらけとかなら無理に変える必要はなし)
特に複雑な非同期処理が必要なときは開発しやすくなると思います

使い方によっては簡易イベントリスナー(特にViewModelからFragmentへの通信)もできたり、LiveDataよりも非同期処理で多岐に扱えるので知っておくと便利です

Discussion