通俗易懂 + Android 开发实战的方式,详细讲讲 Kotlin 中的 StateFlow

一、一句话理解 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 的首选方案。

相关推荐
峰哥的Android进阶之路1 小时前
Kotlin面试题总结
android·开发语言·kotlin
froginwe111 小时前
RSS 语法:全面解析与优化指南
开发语言
美摄科技1 小时前
android短视频sdk,灵活集成,快速上线!
android·音视频
佳哥的技术分享1 小时前
图形化android可视化开机观测工具bootchart
android
杨筱毅1 小时前
【底层机制】 Android ION内存分配器深度解析
android·底层机制
abner.Li2 小时前
基于AOSP11创建一个能用emulator启动的android tv产品
android
小飞大王6662 小时前
JavaScript基础知识总结(六)模块化规范
开发语言·javascript·ecmascript
qk学算法2 小时前
Collections工具类
java·开发语言