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 显得"多余又限制多"。

相关推荐
robotx2 小时前
安卓线程相关
android
消失的旧时光-19432 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
yuhaiqiang2 小时前
被 AI 忽悠后,开始怀念搜索引擎了?
前端·后端·面试
li星野2 小时前
[特殊字符] Linux/嵌入式Linux面试模拟卷
linux·运维·面试
dalancon3 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon3 小时前
VSYNC 信号完整流程2
android
dalancon3 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
xlp666hub4 小时前
如果操作GPIO可能导致休眠,那么同步机制绝不能采用spinlock
linux·面试
li星野4 小时前
RTOS面试完整模拟题(嵌入式系统方向)
arm开发·面试·职场和发展
用户69371750013844 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能