StateFlow、SharedFlow 和LiveData区别

688 阅读3分钟

首先出一道题目开始

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 的区别

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

总结

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

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

//最佳实践
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 显得“多余又限制多”。