Kotlin Flowの基礎と使い分けをまとめてみた【StateFlow・SharedFlowも解説】
こんにちは、Friegenです。
今回は「Flow」について整理していきます。最近のAndroid開発では、もう欠かせない存在ですよね。
ただ、「Flowって種類が多くてよく分からない…」という声もよく聞きます。
この記事では、Flowの基本から、StateFlow・SharedFlowの違い、使い分け方までまとめて紹介します。
Flowとは?
まず、Flowは簡単に言うと「非同期で連続するデータを扱う仕組み」です。
RxJavaの Observable
に近いですが、Kotlin標準のコルーチンと統合されていて、より軽量です。
例えば「サーバーからデータを取得して、UIに順に表示する」といった場面で便利です。
fun simpleFlow(): Flow = flow {
for (i in 1..3) {
delay(1000)
emit(i)
}
}
// collectで受け取る
GlobalScope.launch {
simpleFlow().collect { value ->
Log.d("FlowSample", "値 = $value")
}
}
この例では、1秒ごとに1, 2, 3 と値をemitして、collect側で受け取っています。
Cold Flow と Hot Flow の違い
Flowには大きく分けて2種類あります。
- Cold Flow: collectされるたびに最初から実行される
- Hot Flow: 常にデータを流していて、途中から購読すると最新値や途中の値を受け取る
例えば上の simpleFlow()
はCold Flowです。collect()
するたびに新しく1,2,3をemitします。
一方、StateFlow
やSharedFlow
はHot Flowです。
すでに流れているデータを途中から購読してもOK。
StateFlowとは?
StateFlow は「常に最新の状態を保持するFlow」です。
LiveDataに近い動きをします。
// ViewModel内
private val _uiState = MutableStateFlow("初期状態")
val uiState: StateFlow<String> = _uiState
fun updateState(newText: String) {
_uiState.value = newText
}
そしてUI側でcollectします:
lifecycleScope.launch {
viewModel.uiState.collect { state ->
binding.textView.text = state
}
}
StateFlowのポイント:
- 常に最新値(state)を保持する
- 新しいcollectが来た時も直近の値を即座に受け取れる
- 初期値が必須
UIの状態管理(ボタン押下、入力フォームなど)に最適です。
SharedFlowとは?
SharedFlow は「イベントの発火・共有」に使われます。
StateFlowのように「状態」ではなく「出来事(Event)」を扱いたい時に便利です。
// ViewModel内
private val _eventFlow = MutableSharedFlow<String>()
val eventFlow = _eventFlow.asSharedFlow()
fun sendEvent(message: String) {
viewModelScope.launch {
_eventFlow.emit(message)
}
}
そしてUI側:
lifecycleScope.launch {
viewModel.eventFlow.collect { msg ->
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
}
SharedFlowの特徴:
- 値を保持しない(初期値なし)
- イベント的に扱える
- 複数のcollect先に同時に通知可能
ログイン成功イベント、ダイアログ表示トリガーなどに向いています。
StateFlowとSharedFlowの使い分け
項目 | StateFlow | SharedFlow |
---|---|---|
用途 | UI状態の保持 | 一度きりのイベント通知 |
初期値 | 必要 | 不要 |
最新値保持 | あり | なし |
例 | ボタンON/OFF、フォーム入力 | Toast、ナビゲーションイベント |
Flowの変換・操作関数も押さえておこう
Flowは、Rxと同じようにmapやfilterなども使えます。
flowOf(1, 2, 3, 4, 5)
.filter { it % 2 == 0 }
.map { "偶数: $it" }
.collect { Log.d("FlowSample", it) }
結果:
偶数: 2
偶数: 4
Flowを扱うときは、UIスレッドではなくDispatchers.IO
などで処理を行うようにしましょう。
まとめ
- Flow:非同期データストリームの基本
- Cold Flow:collectごとに新規実行
- Hot Flow:常にデータを流す(StateFlow・SharedFlow)
- StateFlow:状態を保持(UI状態管理向け)
- SharedFlow:イベント通知向け
Flowを使いこなすと、UIと非同期処理のやりとりがかなりスッキリします。
RxJava時代の「購読解除忘れ」も減るので、モダンな開発を目指すなら避けて通れない部分ですね。
次回は、「FlowをRoom・Retrofitと組み合わせる」あたりを掘り下げてみようと思います。
ではまた。
コメント