StateFlow:用于界面的热状态容器
使用 StateFlow 作为响应式状态容器,并在 ViewModels 中观察它。
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 反馈 — 无需本地设置。
此课程中的所有课时
- StateFlow:用于界面的热状态容器
- SharedFlow:事件总线与一次性事件
- 使用 shareIn 与 stateIn 将冷 Flow 转换为热 Flow
- 使用 Turbine 测试 StateFlow 与 SharedFlow