Kotlin Academy · 课时

StateFlow:用于界面的热状态容器

使用 StateFlow 作为响应式状态容器,并在 ViewModels 中观察它。

第 1 / 4 课13 个步骤

StateFlow:用于界面的热状态容器 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

什么是 StateFlow?

StateFlow 是一种持有状态的热流,始终有一个值,并向所有收集器发出更新。在现代 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。开始收集时,最新值会立即发出。

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 只会在值发生变化时发出更新。如果连续两次设置相同的值,只会发出一次——它会合并连续的重复值。

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,由界面观察公开的 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 ViewModels,优先使用 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:将冷流转换为 StateFlow

flow.stateIn(scope, started, initialValue) 会将冷流转换为热 StateFlow,并让所有收集器共享一个订阅。

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 设置相同的值时,会发生什么?

回顾

StateFlow 是一种始终有值且会合并重复值的热流。内部使用 MutableStateFlow,对外公开 StateFlow。使用更新操作进行并发变更,使用 stateIn() 转换冷流。

免费开始

用 AI 导师学习 Kotlin — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「StateFlow:用于界面的热状态容器」课时是免费的吗?

是的 — 「StateFlow:用于界面的热状态容器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「StateFlow:用于界面的热状态容器」这节课中我会学到什么?

使用 StateFlow 作为响应式状态容器,并在 ViewModels 中观察它。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「StateFlow:用于界面的热状态容器」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. StateFlow:用于界面的热状态容器
  2. SharedFlow:事件总线与一次性事件
  3. 使用 shareIn 与 stateIn 将冷 Flow 转换为热 Flow
  4. 使用 Turbine 测试 StateFlow 与 SharedFlow
← 返回 Kotlin Academy