livedata,stateflow,sharedflow,flow啥时候使用

https://www.youtube.com/watch?v=6Jc6-INantQ

学习笔记

在viewmodel中定义三个数据,确保只在内部进行修改,暴露只读的给外部监听

    private val _livedata = MutableLiveData("hello")
    val liveData: LiveData<String> = _livedata

    private val _stateFlow = MutableStateFlow("hello")
    val stateFlow = _stateFlow.asStateFlow()

    private val _sharedFlow = MutableSharedFlow<String>()
    val sharedFlow = _sharedFlow.asSharedFlow()

对应不同的按钮触发数据更改

    fun triggerLiveData() {
        _livedata.value = "livedata"
    }

    fun triggerStateFlow() {
        _stateFlow.value = "stateflow"
    }

    fun triggerFlow(): Flow<String> {
        return flow {
            repeat(5) {
                emit("item $it")
                delay(1000)
            }
        }
    }

    fun triggerSharedFlow(){
        viewModelScope.launch {
            _sharedFlow.emit("SharedFlow")
        }
    }

livedata 和 stateflow比较像,可以hold value,所以初始化的时候,都有传入默认的值

shareflow 和 flow 更像是事件,没有holdvalue

大部分情况下,对于UI的内容,我们使用stateflow比较多

Stateflow是热流,没有订阅者的话,也是会发送事件,然后flow还有很多操作组合等。

另外,当界面横竖屏切换的时候,因为stateflow可以hole值,所以内容还在

Shareflow 和stateflow一样,属于热流,但是Shareflow不会存值,所以横竖屏切换后,之前的值不在了,不会引发事件和更改。所以更适合做消息弹窗

flow是普通的流,是冷流,所以只有当订阅的时候,才开始发送事件,所以当触发triggerFlow的时候我们可以看到数据的发送过程,从0~4,监听到数据变化了,更新textview,当界面切换后,又回到原始的

 private fun subscribeData() {
        mainViewModel.liveData.observe(this) {
            binding.txtTest.text = it
        }

        lifecycleScope.launchWhenStarted {
            mainViewModel.stateFlow.collectLatest {
                //旋转界面值还在
                binding.txtTest.text = it


                //切换界面的时候,回重复出现,因为stateFlow可以缓存值,重新创建会发送
                Snackbar.make(binding.root,
                    it,
                    Snackbar.LENGTH_LONG)
                    .show()
            }
        }

        lifecycleScope.launchWhenStarted {
            mainViewModel.sharedFlow.collectLatest {
                //旋转界面,值不在了,因为 sharedFlow 没有存储值
                binding.txtTest.text = it


                //旋转切换界面,不会重复弹出,除非再次触发emit
                Snackbar.make(binding.root,
                    it,
                    Snackbar.LENGTH_LONG)
                    .show()
            }
        }

    }

    private fun onButtonClick(){
        lifecycleScope.launch {
            mainViewModel.triggerFlow().collectLatest {
                binding.txtTest.text = it
            //点击按钮,会0-4的变化,然后停止,但是界面切换之后,恢复到hello
            }
        }
    }
    private fun onButtonClick2(){
       mainViewModel.triggerSharedFlow()
    }

在加一个stateflow 和Shareflow的对比

 private val _sharedFlow = MutableSharedFlow<String>()
    val sharedFlow = _sharedFlow.asSharedFlow()

    fun triggerStateFlow() {
        _stateFlow.value = "stateflow"
    }


    fun triggerSharedFlow(value:Int){
        viewModelScope.launch {
            _sharedFlow.emit("SharedFlow $value")
        }
    }

    fun collectShareFlow(){
        triggerSharedFlow(1)

        viewModelScope.launch {
            //这里用collect 而不是 collectLatest
            //因为我们不想丢失事件
            sharedFlow.collect{
                delay(1000)
                println("sharedFlow: 1 received $it")
            }
        }
        triggerSharedFlow(2)


        viewModelScope.launch {
            sharedFlow.collect{
                delay(2000)
                println("sharedFlow: 2 received $it")
            }
        }

        triggerSharedFlow(3)
    }

发送了三次时间1、2、3,

1 实在两个订阅之前,2实在两个订阅之间,3 是在两个订阅之后

1  两个订阅都没有收到,因为Shareflow是热流,即使没有订阅也会发送事件,所以在两个订阅开始之前,这个事件就丢失了

2.是在1订阅之后,所以1收到到事件2,

3是在两个订阅之后的,所以两个订阅都收到到了3

`StateFlow` `LiveData` 都是用于在 Android 应用中观察数据变化的组件,尤其常用于 **ViewModel 到 UI 的数据通信**。虽然它们功能相似(都是“状态流”),但设计哲学、使用场景底层机制有显著区别。 下面从多个维度详细对比 `Kotlin` 中的 `StateFlow` 与 `LiveData`。 --- ### 一、基本定义 | 类型 | 来源 | 说明 | |------|------|------| | `LiveData` | Android Jetpack(AndroidX) | 生命周期感知的可观察数据持有者 | | `StateFlow` | Kotlin Coroutines Flow | 状态共享的冷流(Cold Flow),需收集(collect)才能发射数据 | > ⚠️ 注意:`StateFlow` 是 `SharedFlow` 的一个特例,专为“状态”建模。 --- ### 二、核心区别对比 | 特性 | LiveData | StateFlow | |------|--------|----------| | 所属技术栈 | Android 架构组件 | Kotlin 协程 (kotlinx.coroutines) | | 是否生命周期感知 | ✅ 是(自动暂停/恢复观察) | ❌ 否(需要手动配合 Lifecycle 或使用 `lifecycleScope.launchWhenStarted`) | | 运行线程 | 主线程更新(postValue 可跨线程) | 可在任意线程发射(不自动切换线程) | | 数据类型 | 持有最新值(粘性事件) | 持有当前状态(必须初始化) | | 初始化要求 | 可以 null 开始 | 必须提供初始值(如 `MutableStateFlow(0)`) | | 背压处理 | 不支持 | 支持(通过缓冲策略) | | 订阅方式 | `observe(LifecycleOwner, observer)` | `collect { }`(通常在协程中) | | 多播能力 | 自带多播(多个观察者共享) | 多播(所有收集者共享同一实例) | | 内存泄漏风险 | 低(自动解绑) | 中等(若未正确绑定生命周期) | | Kotlin 友好性 | 一般(Java 兼容优先) | ✅ 极佳(原生 Kotlin API,DSL 支持) | --- ### 三、代码示例对比 #### 示例:从 ViewModel 发送用户登录状态给 UI ##### 使用 `LiveData` ```kotlin // UserViewModel.kt class UserViewModel : ViewModel() { private val _userLoggedIn = MutableLiveData<Boolean>() val userLoggedIn: LiveData<Boolean> get() = _userLoggedIn fun login() { viewModelScope.launch { // 模拟网络请求 delay(1000) _userLoggedIn.value = true } } } // Activity 中观察 viewModel.userLoggedIn.observe(this) { isLoggedIn -> if (isLoggedIn) { textView.text = "已登录" } } ``` ✅ 自动绑定生命周期,无需担心内存泄漏。 --- ##### 使用 `StateFlow` ```kotlin // UserViewModel.kt class UserViewModel : ViewModel() { private val _userLoggedIn = MutableStateFlow(false) val userLoggedIn: StateFlow<Boolean> get() = _userLoggedIn fun login() { viewModelScope.launch { // 模拟异步操作 delay(1000) _userLoggedIn.value = true } } } // Activity 中收集(必须在协程中) lifecycleScope.launchWhenStarted { viewModel.userLoggedIn.collect { isLoggedIn -> if (isLoggedIn) { textView.text = "已登录" } } } ``` ⚠️ 关键点: - `collect` 必须在协程中执行。 - 使用 `launchWhenStarted` 确保只在 `STARTED` 状态后才开始收集,避免内存泄漏或无效刷新。 - `launchWhenResumed` 更严格,只在 `RESUMED` 时运行。 --- ### 四、关键特性详解 #### 1. **初始值要求** ```kotlin val flow = MutableStateFlow("hello") // ✅ 必须传初始值 val liveData = MutableLiveData<String>() // ✅ 可以为空,后续设置 ``` 这意味着 `StateFlow` 总是有“当前状态”,而 `LiveData` 可能一开始没有值。 #### 2. **粘性事件(Sticky Event)** 两者都具有粘性:当观察者开始观察时,会立即收到最新的值。 ```kotlin // 如果之前设置了 true _liveData.value = true // 此时再 observe,仍会收到 true liveData.observe(this) { ... } // 收到 true ``` 同样地: ```kotlin _stateFlow.value = "new" // 新的 collect 会立即收到 "new" collect { value -> ... } // 收到 "new" ``` 这是与普通 `Flow` 的重要区别(普通 `Flow` 不粘性)。 #### 3. **线程安全性** - `LiveData`: `.value` 更新必须在主线程;子线程要用 `postValue()`。 - `StateFlow`: `.value` 可以在任何线程修改,但如果你在非主线程更新 UI,仍需切回主线程。 ```kotlin // StateFlow 在后台线程更新没问题 _someJobInIoDispatcher { _stateFlow.value = "updated" } ``` 但 UI 更新仍需: ```kotlin lifecycleScope.launchWhenStarted { viewModel.stateFlow.collect { state -> withContext(Dispatchers.Main) { textView.text = state } } } ``` 或者更优雅地,在 ViewModel 中就处理好线程: ```kotlin fun loadUser() { viewModelScope.launch { val user = repository.fetchUser() // IO _stateFlow.value = user.name // 自动在主线程(因为用了 viewModelScope) } } ``` --- ### 五、何时使用哪个? | 场景 | 推荐使用 | |------|-----------| | 项目使用 Kotlin + 协程 | ✅ `StateFlow` | | 已使用 RxJava | ❌ 建议过渡到 `Flow` 或保留 `Observable` | | Java 项目 | ✅ `LiveData`(Kotlin Flow 不适合 Java) | | MVVM 架构中的 UI 状态暴露 | ✅ 两者皆可,推荐 `StateFlow`(新项目) | | 需要生命周期安全自动管理 | ✅ `LiveData` 更省心;`StateFlow` 需搭配 `lifecycleScope` | | 配合 Jetpack Compose | ✅ `StateFlow` 更自然(`.collectAsState()`) | | 流式变换操作(map/filter/debounce) | ✅ `StateFlow` + `flow.transform` 更强大 | | 简单数据通知,轻量需求 | ✅ `LiveData` 足够 | --- ### 六、最佳实践建议 #### ✅ 推荐现代 Android 开发使用: ```kotlin // ViewModel class MyViewModel : ViewModel() { private val _uiState = MutableStateFlow(UserUiState()) val uiState: StateFlow<UserUiState> = _uiState fun refresh() { viewModelScope.launch { try { val userData = repository.load() _uiState.value = _uiState.value.copy(data = userData, loading = false) } catch (e: Exception) { _uiState.value = _uiState.value.copy(error = e.message, loading = false) } } } } // 在 Activity / Fragment 中 lifecycleScope.launchWhenStarted { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { state -> updateUi(state) } } } ``` 📌 使用 `repeatOnLifecycle` 是关键,确保流收集不会在后台持续运行。 --- ### 七、总结 | 维度 | LiveData | StateFlow | |------|---------|----------| | 易用性(生命周期) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐(需手动控制) | | 功能丰富性 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐(结合 Flow 操作符) | | Kotlin 协程集成 | ⭐⭐ | ⭐⭐⭐⭐⭐ | | 性能 | 良好 | 更高效(轻量级 Flow 实现) | | 未来趋势 | 稳定但逐渐被替代 | ✅ Google 推荐方向(尤其 Compose) | > 🔔 **Google 官方建议**:对于新的 Kotlin 项目,优先使用 `StateFlow` 或 `SharedFlow` 替代 `LiveData`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值