Flowとは?(ざっくりと)
“Flow”という英単語を日本語に訳すと『流れ』になりますが、Kotlinにおける Flow もまさにデータの流れを把握・管理するためのものになります。
整数や文字列などのデータ(値)を変数に代入するのは簡単ですが、単に変数に代入するだけでは、データの移り変わり(=流れ)を把握することができません。
たとえば、ある商品の在庫数を管理するための変数 stock
に、在庫数(整数)を代入するケースを考えてみましょう。在庫数の初期値は100であるとします。
基本的すぎると感じるかもしれませんが、この状況をコードで表すと次のようになります。
var stock = 100
println(stock) // 100
その後、在庫から商品が5つ取り出されたとすれば、コードは次のように表現できます。
var stock = 100
stock -= 5
println(stock) // 95
一見、何の問題もないように思えますが、このように単に変数に代入される値を更新していくだけでは、変数の値がいつ・どのように変化したのかを把握することが困難になります。上の例では、初期値(100)から5を引いただけなのでわざわざデータの流れを追う必要はないと言えますが、現実では『いつ・どれだけ在庫が増えた(or減った)のか』をしっかり把握しなければなりません。
そういったニーズにバッチリ応えるためのKotlinの機能が、Flow ということになります。
Flowを利用することで、変数が保持する値だけでなく、いつ値が更新されたのか、データはどのように変化したのか…などを知ることができるようになります。
なので、Flowは主にUI状態の管理・更新や、リアルタイムデータの取得と処理などに利用されます。
Flow(コールドストリーム)の基本的な使用例
Flowの役割を把握できたら、さっそく実際にFlowを使ったコードを書いてみましょう。引き続き、『商品の在庫数を管理する』という状況を例にしてみたいと思います。
Flowを利用するには、まず Flow
を作成する必要があります。Flowの作成方法は色々ありますが、ここでは flow()
を使ってみましょう。また、Flow
における値の放出には emit()
を使うことができます。
以上を踏まえて在庫数(stock)の変化をFlowで表すと、次のようになります。
var stock = 100 // Initial stock value
val stockFlow = flow {
// Emitting stock changes over time
emit(10) // Stock increased by 10
emit(-3) // Stock decreased by 3
emit(5) // Stock increased by 5
}
この例では、まず在庫が10個増え、その後3個減って、最後にまた5個増える…という流れになっています。
現時点では単に上記の流れを stockFlow
が保持しているだけなので、保持されている流れを集計する必要があります。値の集計には collect()
を使います。値を集計する際に、変数 stock
に在庫数の変化を反映させていきましょう。
コードは下のようになります。
// Collecting the flow and updating the stock
stockFlow.collect { change ->
stock += change
println("Stock after change: $stock")
}
これにより、最終的な値(在庫数)だけでなく、値の移り変わりを捉えることができるようになります。
コードの全体像は次のようになります。
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
var stock = 100 // Initial stock value
val stockFlow = flow {
// Emitting stock changes over time
emit(10) // Stock increased by 10
emit(-3) // Stock decreased by 3
emit(5) // Stock increased by 5
}
// Collecting the flow and updating the stock
stockFlow.collect { change ->
stock += change
println("Stock after change: $stock")
/* Outputs:
* Stock after change: 110
* Stock after change: 107
* Stock after change: 112
*/
}
println("Final stock: $stock") // 112
}
さて、この例では任意のタイミングで colleclt()
を使い、値の集計を行いました。この手法はデータの流れを常に監視し、リアルタイムで把握するためのものではありません。したがって、この Flow
は『コールドストリーム』と呼ばれます。
しかし、UI状態の監視などはリアルタイムで値の変更が監視される必要があり、結果が画面に反映されなければなりません。常に値の変化を監視し、リアクティブに処理を行うための Flow
はコールドストリームに対して『ホットストリーム』と呼ばれます。
次のステップでは、ホットストリームの簡単な例を確認していきましょう。
MutableStateFlow(ホットストリーム)の基本的な使用例
ここでは、UI状態をスイッチで管理する場合を例にしてみたいと思います。オンとオフを切り替えるスイッチが用意されており、それぞれの状態を真偽値(true/false)で管理するとします。
ホットストリームは、MutableStateFlow
を使うことで簡単に利用することができます。初期値をオフ(false)にする場合は、MutableStateFlow(false)
と定義します。また、StateFlowとして定義されたFlowは、value
プロパティで値を得ることができます。。
以上を踏まえると、UI状態をオン・オフで切り替える場合のコードは次のように表すことができます。
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val uiState = MutableStateFlow(false) // initial state value
uiState.value = true // change to true
println(uiState.value) // true
uiState.value = false // change to false
println(uiState.value) // false
}
これだけ見ると、『普通に変数(var)に代入するのと何が違うの?』と思うかもしれません。実際、Kotlin Playgroundなどの簡易的なコード実行環境では MutableStateFlow
の機能は確認しづらく、イメージが湧きづらいです。
しかし、既に説明したように、MutableStateFlow
として定義することでこの値は常に監視されるため、値の変化をリアルタイムで把握・処理することができます。MutableStateFlow
のような機能を目で見て確かめたい場合は、シンプルなAndroidアプリを作ってみるのがオススメです。
とは言え、Kotlinにおける Flow
や StateFlow
の基本を理解するだけであれば、そこまでする必要はなく、次のポイントさえ押さえておくと良いかと思います。
- 最新の値だけでなく、値の変化も把握できる
- ホットストリーム(StateFlow)では値の変化が常に監視されるため、リアクティブな処理が簡単にできる
最後に、Flow
を理解するにはKotlinにおける非同期処理(Coroutine)の基本を理解しておくことも重要です。Coroutineに関しては、こちらの記事で解説しているので、興味があればぜひご覧ください!