一、一句话理解 StateFlow
StateFlow是一个"永远有值"的 Flow,专门用来表示随时间变化的状态(比如加载中、成功、失败),并且会自动把最新状态推送给所有观察者。
你可以把它想象成一个:
- 带默认值的"状态盒子"
- 每次你往里放新值,所有正在观察它的人(比如 UI)都会立刻收到更新
- 而且新来的观察者也能立刻拿到当前最新的值(不像普通 Flow 需要等下次发射)
二、为什么需要 StateFlow?(对比普通 Flow)
| 特性 | 普通 Flow |
StateFlow |
|---|---|---|
| ❄️ 冷/热 | 冷流(每次 collect 才开始) | 热流(一直存在,有初始值) |
| 🧠 记住最新值 | ❌ 不记 | ✅ 总是记住最新值 |
| 👥 多个观察者 | 每个 collect 独立执行 | ✅ 共享同一个状态,节省资源 |
| 📦 初始值 | 没有 | ✅ 必须提供初始值 |
| 🔄 重复值 | 可能重复发射 | 默认会跳过重复值(可配置) |
👉 在 Android 中,UI 状态(如用户信息、列表数据、加载状态)天然就是"有当前值"的,所以 StateFlow 比普通 Flow 更合适!
三、基本用法:怎么创建和使用?
1. 在 ViewModel 中定义 StateFlow(推荐方式)
class UserViewModel : ViewModel() {
// 私有 MutableStateFlow,用于内部修改状态
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
// 对外暴露只读的 StateFlow
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
init {
loadUser()
}
private fun loadUser() {
viewModelScope.launch {
try {
val user = userRepository.getUser()
_uiState.value = UiState.Success(user) // 更新状态
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
}
// 状态密封类(前面学过的!)
sealed class UiState {
object Loading : UiState()
data class Success(val user: User) : UiState()
data class Error(val message: String) : UiState()
}
2. 在 Activity / Fragment / Compose 中收集状态
✅ Jetpack Compose 示例:
@Composable
fun UserProfileScreen(viewModel: UserViewModel) {
val uiState by viewModel.uiState.collectAsState() // 自动转为 State
when (uiState) {
is UiState.Loading -> CircularProgressIndicator()
is UiState.Success -> Text("Hello, ${uiState.user.name}!")
is UiState.Error -> Text("Error: ${uiState.message}")
}
}
✅ View 系统(Activity/Fragment)示例:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { state ->
when (state) {
is UiState.Loading -> showProgressBar()
is UiState.Success -> updateUI(state.user)
is UiState.Error -> showError(state.message)
}
}
}
}
🔔 注意:在 View 系统中要用
repeatOnLifecycle避免内存泄漏!
四、StateFlow 的三大核心特性
1. 必须有初始值
val state = MutableStateFlow(0) // ✅ OK
// val state = MutableStateFlow() // ❌ 编译错误!必须给初始值
2. 自动去重(conflation)
val counter = MutableStateFlow(0)
counter.value = 1
counter.value = 1 // 这个不会触发下游更新(因为值没变)
counter.value = 2 // 会触发更新
如果你想允许重复值,可以用
distinctUntilChanged { false }或改用SharedFlow。
3. 新订阅者立即收到最新值
- 即使你在 10 秒后才开始 collect,也能立刻拿到当前状态。
- 非常适合 UI:页面回来时自动恢复状态。
五、StateFlow vs LiveData(老朋友对比)
| 功能 | LiveData |
StateFlow |
|---|---|---|
| 生命周期感知 | ✅(自动 stop/start) | ❌(需配合 repeatOnLifecycle) |
| Kotlin 协程支持 | ⚠️ 弱(需 liveData builder) | ✅ 原生支持 |
| 空安全 | ❌ 可为 null | ✅ 非空(除非你显式用 StateFlow<User?>) |
| 转换操作符 | 有限(map, switchMap) | ✅ 完整 Flow 操作符(map, filter...) |
| 测试 | 较难 | ✅ 易于测试(纯 Kotlin) |
| 推荐 | 旧项目 | ✅ 新项目首选 |
📌 Google 官方现在更推荐在新项目中使用
StateFlow(尤其搭配 Compose)。
六、常见问题 & 最佳实践
✅ 最佳实践1:对外暴露只读 StateFlow
private val _uiState = MutableStateFlow(...)
val uiState: StateFlow<...> = _uiState.asStateFlow()
防止外部直接修改状态!
✅ 最佳实践2:用 sealed class 表示状态
避免 null 和模糊状态,让 when 分支全覆盖。
✅ 最佳实践3:在 Repository 层也用 StateFlow
比如 Room 数据库查询可以直接返回 Flow,在 ViewModel 中转成 StateFlow:
val users: StateFlow<List<User>> =
userRepository.getAllUsers()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000), // 5秒内无观察者则停止
initialValue = emptyList()
)
stateIn是把普通 Flow 转成 StateFlow 的快捷方式!
七、总结一句话
StateFlow是 Kotlin 协程生态中专为"UI 状态管理"设计的热流,它始终持有最新值、自动去重、支持多观察者,是现代 Android 开发(尤其是 Jetpack Compose)中替代 LiveData 的首选方案。