0Pricing
Kotlin Academy · レッスン

StateFlow:UI向けのHotな状態保持

StateFlowをリアクティブな状態コンテナとして使い、ViewModelで監視します。

「StateFlow:UI向けのHotな状態保持」はCoddyKit上の無料Kotlin Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはKotlin Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Kotlin Academyコースには全4レッスンが含まれています。

StateFlow とは

StateFlow は、常に値を持ち、すべてのコレクターに更新を発行する、状態を保持するホットな Flow です。最新の Kotlin アーキテクチャでは LiveData に置き換わるものです。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val stateFlow = MutableStateFlow(0)  // initial value
    println("Current: ${stateFlow.value}")
    stateFlow.value = 42
    println("Updated: ${stateFlow.value}")
}

MutableStateFlow と StateFlow の違い

MutableStateFlow は内部で使用する可変の実装です。外部のオブザーバーには、キャストするか .asStateFlow() を使用して、読み取り専用の StateFlow を公開します。

import kotlinx.coroutines.flow.*
class CounterViewModel {
    private val _count = MutableStateFlow(0)  // mutable internally
    val count: StateFlow<Int> = _count.asStateFlow()  // read-only externally
    fun increment() { _count.value++ }
    fun decrement() { _count.value-- }
}

StateFlow の収集

StateFlow は他の Flow と同じように収集します。収集を開始すると、最新の値がすぐに発行されます。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val state = MutableStateFlow("loading")
    launch {
        state.collect { println("State: $it") }
    }
    delay(50); state.value = "success"
    delay(50); state.value = "idle"
    delay(50)
    coroutineContext.cancelChildren()
}

StateFlow は重複値をまとめる

StateFlow は値が変化した場合にのみ発行します。同じ値を2回設定すると、発行は1回だけになります。連続する重複値をまとめるためです。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = MutableStateFlow("A")
    launch {
        flow.collect { println("Received: $it") }
    }
    delay(50)
    flow.value = "A"  // no emission — same value
    flow.value = "B"  // emits
    flow.value = "B"  // no emission — same value
    flow.value = "C"  // emits
    delay(50)
    coroutineContext.cancelChildren()
}

ViewModel での StateFlow

ViewModel の標準的なパターンは、ビジネスロジックが非公開の MutableStateFlow を変更し、UI が公開された StateFlow を監視する形です。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class UiState { object Loading : UiState(); data class Success(val data: String) : UiState(); data class Error(val msg: String) : UiState() }
class MyViewModel(scope: CoroutineScope) {
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    init {
        scope.launch {
            delay(100)  // simulate load
            _uiState.value = UiState.Success("Hello!")
        }
    }
}

アトミックな変更には update()

update { } を使用すると、現在の値に基づいて状態をアトミックに更新できます。これは同時に更新が行われる場合に重要です。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val counter = MutableStateFlow(0)
    List(100) {
        launch(Dispatchers.Default) {
            counter.update { it + 1 }  // thread-safe atomic update
        }
    }.forEach { it.join() }
    println("Counter: ${counter.value}")  // 100
}

楽観的な更新には compareAndSet

compareAndSet(expect, update) は、現在の値が expect と一致する場合にのみ新しい値を設定します。楽観的並行性制御に役立ちます。

import kotlinx.coroutines.flow.*
fun main() {
    val state = MutableStateFlow("idle")
    val changed = state.compareAndSet("idle", "loading")
    println("Changed: $changed, Value: ${state.value}")  // true, loading
    val changed2 = state.compareAndSet("idle", "error")  // won't change
    println("Changed: $changed2, Value: ${state.value}") // false, loading
}

StateFlow と LiveData の違い

StateFlow は Android のライフサイクルなしで動作します。LiveData はライフサイクルを認識しますが、Android 専用です。共有 KMP ViewModel には StateFlow を推奨します。

import kotlinx.coroutines.flow.*
// StateFlow: pure Kotlin, works in commonMain
// val state = MutableStateFlow("value")

// LiveData: Android-only, lifecycle-aware
// val liveData = MutableLiveData("value")

// In Compose, collect StateFlow with:
// val state by viewModel.uiState.collectAsState()
fun main() { println("StateFlow: multiplatform; LiveData: Android-only") }

stateIn:Cold Flow を StateFlow に変換する

flow.stateIn(scope, started, initialValue) は Cold Flow をホットな StateFlow に変換し、すべてのコレクターで1つのサブスクリプションを共有します。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val cold = flow {
        println("Fetching...")
        delay(100)
        emit("data")
    }
    val hot: StateFlow<String> = cold.stateIn(
        scope = this,
        started = SharingStarted.Lazily,
        initialValue = "loading"
    )
    launch { hot.collect { println("A: $it") } }
    launch { hot.collect { println("B: $it") } }
    delay(200)
    coroutineContext.cancelChildren()
}

SharingStarted の戦略

Eagerly:すぐに開始します。Lazily:最初のコレクターで開始します。WhileSubscribed(stopTimeout):コレクターがいなくなると停止し、コレクターが登録されると再開します。

import kotlinx.coroutines.flow.*
// Eagerly: upstream starts right away
// SharingStarted.Eagerly

// Lazily: waits for first subscriber
// SharingStarted.Lazily

// WhileSubscribed: stops 5s after last subscriber
// SharingStarted.WhileSubscribed(5000)

// Typical ViewModel usage:
// val uiState = repo.dataFlow.stateIn(viewModelScope, WhileSubscribed(5000), Loading)

派生状態の監視

map + stateIn を使用して、既存の StateFlow から新しい StateFlow を派生させます。派生したフローは、元のフローが変化するたびに更新されます。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val count = MutableStateFlow(5)
    val doubled = count.map { it * 2 }.stateIn(this, SharingStarted.Eagerly, 10)
    println(doubled.value)  // 10
    count.value = 7
    delay(50)
    println(doubled.value)  // 14
    coroutineContext.cancelChildren()
}

クイックチェック

StateFlow に同じ値を2回設定するとどうなりますか。

振り返り

StateFlow は、常に値を持ち、重複値をまとめるホットな Flow です。内部では MutableStateFlow を使用し、外部には StateFlow を公開します。並行した変更には update() を使用し、Cold Flow の変換には stateIn() を使用します。

よくある質問

「StateFlow:UI向けのHotな状態保持」レッスンは無料ですか?

はい。「StateFlow:UI向けのHotな状態保持」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Kotlin Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Kotlin Academyコースには全4レッスンが含まれています。

「StateFlow:UI向けのHotな状態保持」で何を学びますか?

StateFlowをリアクティブな状態コンテナとして使い、ViewModelで監視します。 ブラウザで直接実行するハンズオンコードでKotlin Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Kotlin Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのKotlin Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「StateFlow:UI向けのHotな状態保持」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このKotlin Academyレッスンでコードを書いて実行できますか?

はい。すべてのKotlin Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. StateFlow:UI向けのHotな状態保持
  2. SharedFlow:イベントバスとワンショットイベント
  3. shareInとstateInでCold FlowをHotに変換する
  4. TurbineによるStateFlowとSharedFlowのテスト
← Kotlin Academyに戻る