Android StateFlow详解

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/121913352
本文出自【赵彦军的博客】

系列文章

Kotlin实战指南二十:flow
Android SharedFlow详解
Android StateFlow详解

接口结构

在这里插入图片描述

一、冷流还是热流

我先给大家介绍一下概念,冷流和热流。

如果之前了解过 Kotlin 的协程,那么你就有可能知道 Flow 是冷流,什么是冷流?简单来说,如果 Flow 有了订阅者 Collector 以后,发射出来的值才会实实在在的存在于内存之中,这跟懒加载的概念很像。

与之相对的是热流,我们即将介绍的 StateFlow SharedFlow 是热流,在垃圾回收之前,都是存在内存之中,并且处于活跃状态的。

StateFlow

对于 StateFlow,官方的介绍是:

StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新。

乍一看,和 Flow 没什么区别,但是你看使用代码,却是有很大的区别。

1. StateFlow使用

第一步:创建 MutableStateFlow 并设置初始化的值。

class MainViewModel : ViewModel() {
    val selected = MutableStateFlow<Boolean>(false)
}

第二步:同 Flow 一样,使用 collect 方法:

lifecycleScope.launch {
    viewModel.selected.collect {
        // ... 引起UI发生的变化
        // 比如 某个按钮是否选中状态
    }
}

第三步:可以给 selected设置值,从而引起 Ui 层的变化:

class MainViewModel : ViewModel() {
    val selected = MutableStateFlow<Boolean>(false)
    fun doSomeThing(value: Boolean) {
        selected.value = value
    }
}

普通的 Flow,是不具备 selected.value = value 这种能力的。

仔细观察一下,这个使用体验完全跟 LiveData 一样,所以它的使用场景和 LiveData 也很类似。

2. 和LiveData比较

那么 StateFlowLiveData 有什么区别吗?

有两点区别:

  • 第一点,StateFlow 必须有初始值,LiveData 不需要。
  • 第二点,当 View 变为 STOPPED 状态时,LiveData.observe() 会自动取消注册使用方,而从 StateFlow 或任何其他数据流收集数据则不会取消注册使用方。

对于 StateFlow 在界面销毁的时仍处于活跃状态,有两种解决方法:

  • 使用 ktxFlow 转换为 LiveData
  • 在界面销毁的时候,手动取消(这很容易被遗忘)。
class LatestNewsActivity : AppCompatActivity() {
    ...
    // Coroutine listening for UI states
    private var uiStateJob: Job? = null

    override fun onStart() {
        super.onStart()
        // Start collecting when the View is visible
        uiStateJob = lifecycleScope.launch {
            latestNewsViewModel.uiState.collect { uiState -> ... }
        }
    }

    override fun onStop() {
        // Stop collecting when the View goes to the background
        uiStateJob?.cancel()
        super.onStop()
    }
}

3、如果值发送得太快,collect 时只会收到最后一个值,这个特性和 LiveData 也是一样的

举例1:

class Te {

    private val stateFlow = MutableStateFlow(1)

    fun test() {
        GlobalScope.launch {
            stateFlow.collect {
                println("stateFlow 接收:$it")
            }
        }

        GlobalScope.launch {
            delay(100)
            stateFlow.value = 2
            delay(100)
            stateFlow.value = 3
            delay(100)
            stateFlow.value = 4
            delay(100)
            stateFlow.value = 5
        }
    }
}

日志:

System.out: stateFlow 接收:1
System.out: stateFlow 接收:2
System.out: stateFlow 接收:3
System.out: stateFlow 接收:4
System.out: stateFlow 接收:5

举例2:把 delay 都去掉

class Te {

    private val stateFlow = MutableStateFlow(1)

    fun test() {
        GlobalScope.launch {
            stateFlow.collect {
                println("stateFlow 接收:$it")
            }
        }

        GlobalScope.launch {
            stateFlow.value = 2
            stateFlow.value = 3
            stateFlow.value = 4
            stateFlow.value = 5
        }
    }
}

日志:

System.out: stateFlow 接收:1
System.out: stateFlow 接收:5

StateFlow 特性总结

  • 订阅过程:在 StateFlow 中,可以有多个订阅者。每个 FlowCollecter类型的对象都被称为订阅者。调用StateFlow类型对象的collect方法会触发订阅。正常情况下,订阅不会自动结束,但订阅者可以取消订阅,当订阅者所在的协程被取消时,订阅过程就会取消。

  • 相等判定:在StateFlow中,通过Any#equals方法来判断前后两个数据是否相等。当前后两个数据相等时,数据不会被更新,订阅者也不会处理。

  • 数据缓存:StateFlow必须要有一个初始值。当新订阅者出现时,StateFlow会将最新的数据发射给订阅者。StateFlow只保留最后发射的数据,除此之外不会缓存任何其他的数据。

  • StateFlow并发: StateFlow中所有的方法都是线程安全的,并且可以在多协程并发的场景中使用且不必额外加锁。

  • 如果值发送得太快,collect 时只会收到最后一个值,这个特性和 LiveData 也是一样的

asStateFlow

MutableStateFlow 是双向结构,可以发送数据,也可以接收数据

如果我们要对外暴露一个只能接收的结构,怎么处理?

使用 MutableStateFlow 的扩展函数 ,asStateFlow

/**
 * Represents this mutable state flow as a read-only state flow.
 * 将此可变状态流表示为只读状态流。
 */
public fun <T> MutableStateFlow<T>.asStateFlow(): StateFlow<T> =
    ReadonlyStateFlow(this, null)

使用举例

class MainActivity : AppCompatActivity() {
  private val _clickCountFlow = MutableStateFlow(0)
  var clickCountFlow = _clickCountFlow.asStateFlow()

  override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
 
      //......
       lifecycleScope.launch {
           //clickCountFlow只能接收数据,可以暴露出去给被人用
           clickCountFlow.collect {
         
           }
       }
     
     //_clickCountFlow 发送数据,可以设置为 private ,只能内部使用,不对外暴露
     _clickCountFlow.value = 100
   }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值