一、StateFlow 是什么?
- 属于 Kotlin 协程 Flow 体系
- 粘性可观察状态持有者
- 永远持有最新一个状态
- 新订阅者会立刻收到当前最新值
- 适合:页面 UI 状态管理(MVVM、MVI)
- 线程安全、适合跨协程使用
二、StateFlow 两种类型
1. MutableStateFlow(内部可写)
kotlin
private val _uiState = MutableStateFlow(UiState())
- 只在 ViewModel 内部使用
- 可以修改值、更新状态
2. StateFlow(外部只读)
kotlin
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
- 暴露给 Fragment/Activity
- 只能观察,不能修改
- 保证安全、单向数据流
三、定义步骤(标准模板)
1. 定义状态(必须是 data class)
kotlin
data class UiState(
val isLoading: Boolean = false,
val data: String? = null,
val error: String? = null
)
2. ViewModel 里创建
kotlin
class MyViewModel : ViewModel() {
// 可变(内部)
private val _uiState = MutableStateFlow(UiState())
// 只读(外部)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
}
3. 界面收集(观察)
kotlin
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
// 更新UI
tv.text = state.data
}
}
}
四、更新 StateFlow 的 5 种方法(最重要)
1. update {...} —— 最推荐、线程安全
基于旧值生成新值,原子操作、并发安全
kotlin
_uiState.update { it.copy(isLoading = true) }
2. value = 直接赋值
适合不依赖旧值的覆盖更新非原子,多协程不安全
kotlin
_uiState.value = UiState(isLoading = true)
3. getAndUpdate { ... }
原子更新,返回更新前的值
kotlin
val oldState = _uiState.getAndUpdate { it.copy(isLoading = true) }
4. updateAndGet { ... }
原子更新,返回更新后的值
kotlin
val newState = _uiState.updateAndGet { it.copy(isLoading = true) }
5. compareAndSet(expect, newValue)
底层原子方法,手动判断更新
kotlin
_uiState.compareAndSet(oldValue, newValue)
五、方法对比表(必背)
| 方法 | 线程安全 | 原子 | 基于旧值 | 返回值 | 推荐度 |
|---|---|---|---|---|---|
| update | ✅ | ✅ | ✅ | Unit | ⭐⭐⭐⭐⭐ |
| value = | ❌ | ❌ | ❌ | Unit | ⭐⭐ |
| getAndUpdate | ✅ | ✅ | ✅ | 旧值 | ⭐⭐⭐⭐ |
| updateAndGet | ✅ | ✅ | ✅ | 新值 | ⭐⭐⭐⭐ |
| compareAndSet | ✅ | ✅ | ✅ | Boolean | ⭐⭐⭐ |
六、为什么必须用 copy ()?
因为:StateFlow 比较引用,不比较内容
kotlin
// 正确:创建新对象
it.copy(isLoading = true)
// 错误:对象没变,UI 不刷新
it.isLoading = true
copy () 作用:
- 复制一个新对象
- 只改你指定的字段
- 其他字段自动保留
七、StateFlow vs SharedFlow(高频面试)
| 特性 | StateFlow | SharedFlow |
|---|---|---|
| 存储数据 | 只存最新 1 个 | 可存多个缓冲 |
| 粘性 | ✅ 新订阅收当前值 | 可配置 |
| 初始值 | 必须给 | 不需要 |
| 用途 | UI 状态管理 | 事件通知 |
| 去重 | 相同值不会发射 | 不会去重 |
八、最佳实践(公司标准规范)
1. 状态必须用 data class
kotlin
data class UiState(...)
2. 私有可变,公开只读
kotlin
private val _uiState = MutableStateFlow(...)
val uiState = _uiState.asStateFlow()
3. 更新状态必须用 update
kotlin
_uiState.update { it.copy(xxx = xxx) }
4. 界面收集必须用:
kotlin
repeatOnLifecycle(Lifecycle.State.STARTED)
5. 不要在 StateFlow 存:
- Activity / Context
- View
- 非数据类(容易内存泄漏)
九、常见坑(90% 的人都踩过)
❌ 错误 1:不使用 copy () → UI 不刷新
kotlin
// 不生效
_uiState.value.isLoading = true
❌ 错误 2:直接暴露 MutableStateFlow
kotlin
// 不安全!外部可随便改
val uiState = MutableStateFlow(...)
❌ 错误 3:不用 repeatOnLifecycle 导致内存泄漏
kotlin
// 错误
lifecycleScope.launch { ... }
// 正确
repeatOnLifecycle(STARTED) { ... }
❌ 错误 4:多协程并发用 value =
kotlin
// 线程不安全
_uiState.value = _uiState.value.copy(...)
十、一句话终极总结
StateFlow = 持有最新状态的安全可观察对象
update () + copy () = 标准更新方式
只读暴露 + repeatOnLifecycle = 标准使用方式