StateFlow、SharedFlow 和LiveData区别

首先出一道题目开始

kotlin 复制代码
class TestActivity : FragmentActivity() {
    val viewModel: TestVM by lazy {
        ViewModelProvider(this).get(TestVM::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.test)
        setupObservers(1)
    }

    override fun onStop() {
        super.onStop()
        viewModel.liveData.value = true
        lifecycleScope.launch {
            viewModel.stateFlow.emit(true)
            viewModel.sharedFlow.emit(true)
            delay(1000L)
            setupObservers(2)
        }
    }

    fun setupObservers(i: Int) {
        viewModel.liveData.observe(this, { it ->
            Log.i("TestModel", "$i liveData observer: $it")
        })

        lifecycleScope.launch {
            viewModel.stateFlow.collect { value ->
                Log.i("TestModel", "$i stateFlow: $value")
            }
        }

        lifecycleScope.launch {
            viewModel.sharedFlow.collect { value ->
                Log.i("TestModel", "$i sharedFlow: $value")
            }
        }
    }
}

class TestVM : ViewModel() {
    val stateFlow = MutableStateFlow(false)
    val sharedFlow =
        MutableSharedFlow<Boolean>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)

    val liveData = MutableLiveData<Boolean>()
}

如上代码所示,进入TestActivity→ 退到后台 → 从后台返回至前台。 你猜日志打印什么?

进入TestActivity

退至后台

后台返回前台

从上面的问题可以得出几个官点:

  1. StateFlow初始化必须要有一个初值,LiveData可有可没有
  2. LiveData只有在可见的时候(onStart→onStop(不包含)),订阅者才能收到值,非可见的时候发生值的变更,只是保存(保存一次),等到可见的时候,才分发给订阅者
  3. 不可见的时候触发值的更新,StateFlow和SharedFlow都会立马回调给观察者,但是LiveData需要变成可见的时候才回调
  4. MutableSharedFlow 可以配置缓存size

LiveData、SharedFlow、StateFlow 的区别

特性 LiveData SharedFlow StateFlow
生命周期感知 ✅ 自动 ❌ 需手动管理 ❌ 需手动管理
必须有初始值 ❌ 可选 ❌ 不需要 ✅ 必须提供
订阅后立即上到上一次值 ✅ 会 ❌ 否 (可配置 (replay)) ✅ 会
多个订阅者 ✅ 支持 ✅ 支持 ✅ 支持
背压处理 ❌ 无 ✅ 可配置缓冲区 ✅ 固定缓冲区(size=1)
线程安全 ✅ 主线程安全 ✅ 协程上下文安全 ✅ 协程上下文安全
数据去重 ❌ 不自动去重 ❌ 不自动去重 ✅ 自动去重
数据发射方式 setValue(), postValue() emit(), tryEmit() emit(), tryEmit()
是否保存状态 ✅ 是 ❌ 否(可配置replay) ✅ 是
观察方式 liveData+lifecycle 协程收集 协程收集
推荐使用场景 简单UI状态更新 一次性事件+使用流式转换 UI状态+使用流式转换

总结

SharedFlow:适用了一次性的事件并且可以使用流式转换

StateFlow: 也是Kotlin官方用来替代LiveData的,因为他支持更复杂的操作,但是Kotlin没有耦合进Android特有的生命周期,无生命周期的感知能力并且需要搭配协程使用。

kotlin 复制代码
//最佳实践
lifecycleScope.launch{
    repeatOnLifeCycle(Lifecycle.State.STARTED){
       stateFlow.collect{ value ->
          textView.text=value
       }
    }
}

使用repeatOnLifeCycle 这样就让stateFlow具备了生命周期感知能力

LiveData: 简单的UI状态更新

大部分情况下使用LiveData就行了,三者使用

思考

为什么MVI 架构中我们看到的使用StateFlow那一套比较多?

1. MVI 想的是「状态是整个界面的唯一真相源」

  • View 不自己存状态。
  • 状态只存在于 ViewModel 的单个对象中。
  • 当你知道这个对象的当前值,你就能100%还原界面。

LiveData 在生命周期变化时,可能自动暂停、延迟派发、自动 re-dispatch,这些"智能操作"会让状态的可预测性下降。 而 StateFlow 不管这些,你自己控制收集时机,所以状态更确定

2.MVI 强调「所有副作用可控、不会重复」

  • LiveData 的"粘性"+"重新变 active 就重新回调"机制,会让你重复执行一次性事件(比如弹两次 Toast)。
  • StateFlow + SharedFlow 明确区分:
    • StateFlow:保存界面状态
    • SharedFlow:发射一次性事件(不会自动重放)

这正符合 MVI 的理念:"状态可重放,副作用不可重放"。
3. MVI 追求「跨平台/可测试的纯逻辑层

LiveData 是 Android 平台的特产(依赖 LifecycleOwnerLooper)。 而 StateFlow 纯 Kotlin 协程实现,既能跑在 Android,也能跑在 JVM/Compose/多平台项目里。

所以在 MVI + Kotlin 协程 体系下,LiveData 显得"多余又限制多"。

相关推荐
JAVA面经实录91718 小时前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
liang_jy20 小时前
Android SparseArray
android·源码
小程故事多_8020 小时前
[大模型面试系列] 多轮对话 Agent 设计实战(含窗口优化 + 工具调用精髓)
人工智能·面试·职场和发展
liang_jy20 小时前
Activity 启动流程扩展篇(一)—— startActivityInner 任务决策全解析
android·源码
NPE~21 小时前
[App逆向]脱壳实战
android·教程·逆向·android逆向·逆向分析
木易 士心1 天前
别再只会用 drawCircle 了!一文搞懂 Android Canvas 底层机制
android
AtOR CUES1 天前
MySQL——表操作及查询
android·mysql·adb
怣疯knight1 天前
安卓App无法增加自定义图片作为图标功能
android
AI人工智能+电脑小能手1 天前
【大白话说Java面试题】【Java基础篇】第24题:Java面向对象有哪些特征
java·开发语言·后端·面试
迦南的迦 亚索的索1 天前
AI_11_Coze_AI面试助手
人工智能·面试·职场和发展