Flow、SharedFlow、StateFlow 傻傻分不清楚

冷流与热流

Flow 与 SharedFlow、StateFlow 最大的区别在于 Flow 是冷流,而 SharedFlow、StateFlow 是热流。那冷流和热流又有什么区别?

冷流中的数据并不是一直存在内存中的,而且当收集的时候,才会产生,存储在内存中,等收集完后就会自动回收。

而热流中的数据是可以即使不收集的时候,也能产生数据,并把产生后就存储在内存中,等到收集完后,再把数据进行回收。

Flow

关于 Flow 的说明以及使用,可以参考「flow 操作符全解析」,这里就不过多进行说明了。

SharedFlow

在说明 SharedFlow 与 StateFlow 的区别前,我们可以先来了解 SharedFlow 是什么?

SharedFlow 就是观察者模式的一个具体实现,可以对观察者进行注册,当被观察者发生改变的时候,就会通知观察者,同时,SharedFlow 还可以存储之前的变化,即当 SharedFlow 变化后,才有观察者注册,这时观察者仍然能够收到之前的变化。

说了这么多,我们先来看看 SharedFlow 的简单使用吧,后续再详细说明。

首先,我们先创建 SharedFlow 对象:

private val sharedFlow = MutableSharedFlow<Int>()

然后进行数据提交更改:

            sharedFlow.emit(0)
            sharedFlow.emit(1)
            sharedFlow.emit(2)

然后进行收集:

            sharedFlow.collect { value ->
                println("value -> $value")
            }

我们来看下日志输出:

    

em??? 怎么啥都没有,这跟说好的不一样啊!

我们来看看 MutableSharedFlow 的构造参数:

public fun <T> MutableSharedFlow(
    replay: Int = 0,
    extraBufferCapacity: Int = 0,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T> {
  • replay:指的是观察者注册后,能够收到多少个之前的通知,默认是为 0。
  • extraBufferCapacity:指的是当被观察者发送速度过快,或者观察者消化消息的速度过慢的时候,数据就会临时存储起来,而这里指的就是额外存储的个数,注意,是额外存储的个数,实际存储的个数是 replay + extraBufferCapacity
  • onBufferOverflow:由于 MutableSharedFlow 的存储空间是固定的,有可能会被超过限制,而 onBufferOverflow 则是标识当超过这个限制的时候,怎么进行处理,目前有三种选项:
    • BufferOverflow.SUSPEND:挂起,默认实现。
    • BufferOverflow.DROP_OLDEST:丢弃最老的值,保留最新的。
    • BufferOverflow.DROP_LATEST:丢弃最新的值,保留最老的。

通过上面的参数的讲解,我们就能够很清楚为什么上面的例子收不到消息了,所以,下面我们通过一个例子,简单测验这些值的功能。

class MainActivity : AppCompatActivity() {

    private val sharedFlow = MutableSharedFlow<Int>(1, 2, BufferOverflow.DROP_OLDEST)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        GlobalScope.launch {
            sharedFlow.emit(0)
            sharedFlow.emit(1)
            sharedFlow.emit(2)

            sharedFlow.collect { value ->
                delay(1000)
                println("value -> $value")
            }
        }

        GlobalScope.launch {
            sharedFlow.emit(3)
            sharedFlow.emit(4)
            sharedFlow.emit(5)
            sharedFlow.emit(6)
        }
    }
}

日志输出为:

I/System.out: value -> 2
I/System.out: value -> 4
I/System.out: value -> 5
I/System.out: value -> 6

我们来逐步分析下日志:

  • 为什么有 2 ?

这是因为 replay 为 1,只会存储一位已发送的数据,所以,最开始发送的数据 0、1、2,只会保留最新的 2。

  • 为什么有 4、5、6 ?

这是因为 MutableSharedFlow 实际的缓存个数为 replay + extraBufferCapacity,也就是 1 + 2 = 3,所以有三个缓存位置,又由于 onBufferOverflow 为 BufferOverflow.DROP_OLDEST,所以,3、4、5、6 中只会保留最新的 4、5、6。

特别说明

有一点需要特别注意的,就是当 collect 运行结束后,即使使用 emit 发送数据,collect 里面的代码也不会执行。

另外,在测验中,我还发现另外一个坑点,就是把 collect 和 emit 放到同一个协程中运行,特别是 emit 在 collect 之后,会发现没有任何输出,就像这样:

        GlobalScope.launch {
            sharedFlow.collect { value ->
                delay(1000)
                println("value -> $value")
            }

            sharedFlow.emit(0)
            sharedFlow.emit(1)
            sharedFlow.emit(2)
        }

其实,这也很容易理解,因为 delay 的时候,该协程都阻塞了,emit 是不会执行到,而当 delay 之后,collect 都执行完成,这时再 emit,自然也不会再执行 collect 里面的代码。

StateFlow

StateFlow 其实就是一个特殊的 SharedFlow,就可以理解为是:

MutableSharedFlow(1,0, BufferOverflow.SUSPEND)

下面我们用个例子来看看是不是这个效果:


class MainActivity : AppCompatActivity() {

    private val stateFlow = MutableStateFlow(0)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        GlobalScope.launch {
            stateFlow.emit(0)
            stateFlow.emit(1)
            stateFlow.emit(2)

            stateFlow.collect { value ->
                delay(1000)
                println("value -> $value")
            }
        }

        GlobalScope.launch {
            stateFlow.emit(3)
            stateFlow.emit(4)
            stateFlow.emit(5)
            stateFlow.emit(6)
        }
    }
}

日志输出:

I/System.out: value -> 2
I/System.out: value -> 6

大家可以根据我在上面的说明,看看能不能去理解为什么日志输出会是这样。

特别说明

  • StateFlow 和 SharedFlow 一样,collect 结束后,再 emit 也不会再次调起 collect 里面的代码,除非重新 collect。
  • StateFlow 除了可以使用 emit 提交更改,也可以使用这种方式进行更改:stateFlow.value = 1
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值