通俗易懂 + 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 的首选方案。

相关推荐
阿巴斯甜15 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker16 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952717 小时前
Andorid Google 登录接入文档
android
黄林晴18 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android