Android SharedFlow详解

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

系列文章

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

什么是SharedFlow

StateFlow 一样,SharedFlow 也是热流,它可以将已发送过的数据发送给新的订阅者,并且具有高的配置性。SharedFlow 是一个接口,继承 Flow

在这里插入图片描述
在使用是,一般使用 MutableSharedFlow

在这里插入图片描述
SharedFlow 有如下特点:

1、是热数据流 ,及时没有接收者,也会发射数据
2、SharedFlow 是 StateFlow 的可配置性极高的泛化数据流。
3、可以有多个接收器,一个数据可以被多个接收

实战

class MainActivity : AppCompatActivity() {

    private val mutableSharedFlow = MutableSharedFlow<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch {

            mutableSharedFlow.collect {
                Log.d("bus-", "观察者1,$it")
            }

            mutableSharedFlow.collect {
                Log.d("bus-", "观察者2,$it")
            }
        }

        //发射数据
        lifecycleScope.launch {
            mutableSharedFlow.emit(System.currentTimeMillis().toString())
        }
    }
}

日志:

D/bus-: 观察者11639392387467

很奇怪,观察者2 没有输出日志。我们的期望是 发射一个数据,可以被两个观察者接收,但实际只有一个观察者接收了数据。

事实上 第二个 观察者的代码就没有执行。也就是 下面这段代码 没有被执行。

 mutableSharedFlow.collect {
     Log.d("bus-", "观察者2,$it")
}

collect 是挂起函数,因为他可以不断的接收值,会不断的挂起,不断的恢复。具体实现在 SharedFlowImpl 中。

在这里插入图片描述

可以这么写,用两个协程去接收值:

class MainActivity : AppCompatActivity() {

    private val mutableSharedFlow = MutableSharedFlow<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch {

            mutableSharedFlow.collect {
                Log.d("bus-", "观察者1,$it")
            }
        }

        lifecycleScope.launch {
            mutableSharedFlow.collect {
                Log.d("bus-", "观察者2,$it")
            }
        }

        //发射数据
        lifecycleScope.launch {
            mutableSharedFlow.emit(System.currentTimeMillis().toString())
        }
    }
}

日志输出:

D/bus-: 观察者11639393316855
D/bus-: 观察者21639393316855

默认无粘性

SharedFlow 默认无粘性的,也就是后面的观察者不能收到前面已经发射的数据。

当然也有api,支持。

在这里插入图片描述

replay 代表重放的数据个数

replay 为0 代表不重放,也就是没有粘性
replay 为1 代表重放最新的一个数据,后来的接收器能接受1个最新数据。
replay 为2 代表重放最新的两个数据,后来的接收器能接受2个最新数据。

示例如下:

class MainActivity : AppCompatActivity() {

    private val mutableSharedFlow = MutableSharedFlow<String>(replay = 2)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch {
            mutableSharedFlow.collect {
                Log.d("bus-", "观察者1,$it")
            }
        }

        lifecycleScope.launch {
            mutableSharedFlow.collect {
                Log.d("bus-", "观察者2,$it")
            }
        }

        //发射数据
        lifecycleScope.launch {
            mutableSharedFlow.emit("a")
            mutableSharedFlow.emit("b")
            mutableSharedFlow.emit("c")
        }

        lifecycleScope.launch {
            delay(2000)
            mutableSharedFlow.collect {
                Log.d("bus-", "观察者3,$it")
            }
        }

    }
}

输出日志

D/bus-: 观察者1,a
D/bus-: 观察者1,b
D/bus-: 观察者2,a
D/bus-: 观察者2,b
D/bus-: 观察者2,c
D/bus-: 观察者1,c
D/bus-: 观察者3,b
D/bus-: 观察者3,c

观察者1 ,观察者2 都接受到了三个数据,分别是 a、b、c
观察者3 是后来加入的,可以接收到最新的两个数据 b、c

extraBufferCapacity 额外的数据缓存池

Flow 存在发送过快,消费太慢的情况,这种情况下,就需要使用缓存池,把未消费的数据存下来。

缓冲池容量 = replay + extraBufferCapacity

如果总量为 0 ,就 Int.MAX_VALUE

在这里插入图片描述

onBufferOverflow

如果指定了有限的缓存容量,那么超过容量以后怎么办?

  • BufferOverflow.SUSPEND : 超过就挂起,默认实现
  • BufferOverflow.DROP_OLDEST : 丢弃最老的数据
  • BufferOverflow.DROP_LATEST : 丢弃最新的数据

什么情况下用 SharedFlow

当你有如下场景时,需要使用 SharedFlow:

  • 发生订阅时,需要将过去已经更新的 n 个值,同步给新的订阅者。
  • 配置缓存策略。
  • 有多个订阅者。

tryEmit

MutableSharedFlow 中缓存数据量超过阈值时,emit 方法和 tryEmit 方法的处理方式会有不同:

  • emit 方法:当缓存策略为 BufferOverflow.SUSPEND 时,emit 方法会挂起,直到有新的缓存空间。
  • tryEmit 方法:tryEmit 会返回一个 Boolean 值,true 代表传递成功,false 代表会产生一个回调,让这次数据发射挂起,直到有新的缓存空间。

将冷流转化为SharedFlow

直接使用官网的代码,方法是使用 Flow 的扩展方法 shareIn

class NewsRemoteDataSource(...,
    private val externalScope: CoroutineScope,
) {
    val latestNews: Flow<List<ArticleHeadline>> = flow {
        ...
    }.shareIn(
        externalScope,
        replay = 1,
        started = SharingStarted.WhileSubscribed() // 启动政策
    )
}

重点是参数三,分别提供了三个启动策略:

  • SharingStarted.WhileSubscribed():存在订阅者时,将使上游提供方保持活跃状态。
  • SharingStarted.Eagerly:立即启动提供方。
  • SharingStarted.Lazily:在第一个订阅者出现后开始共享数据,并使数据流永远保持活跃状态。

总结

Flow 给我的感觉就像古老的印刷术,版面定了就不可更改,不过,该版面可印刷多张内容;StateFlow 给我的感觉就像活字印刷,可以不停的更改版面,也可以使用同一个版面印刷很多内容。

如果你要使用 Flow 记录数据的状态,StateFlowSharedFlow 会是一个不错的选择。StateFlowSharedFlow 提供了在 Flow 中使用 LiveData 式更新数据的能力,但是如果要在 UI 层使用,需要注意生命周期的问题。

StateFlowSharedFlow 相比,StateFlow 需要提供初始值,SharedFlow 配置灵活,可提供旧数据同步和缓存配置的功能。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值