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

相关推荐
工程师老罗16 分钟前
如何在Android工程中配置NDK版本
android
Libraeking4 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位4 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
发现一只大呆瓜6 小时前
虚拟列表:支持“向上加载”的历史消息(Vue 3 & React 双版本)
前端·javascript·面试
JMchen1236 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs6 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob6 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔7 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei9967 小时前
flutter和Android动画的对比
android·flutter·动画
千寻girling7 小时前
Koa.js 教程 | 一份不可多得的 Node.js 的 Web 框架 Koa.js 教程
前端·后端·面试