我是靠谱客的博主 暴躁季节,这篇文章主要介绍StateFlow与SharedFlow(一),现在分享给大家,希望可以做个参考。

简介

因作者语文不好,就不进行繁琐的介绍了.如果照抄其他文章或者官方api也没有意义

可以简单理解为StateFlow和SharedFlow都是Flow的子类.

区别是,Flow是冷流,没有订阅者可以理解为他不保存数据,你在订阅之前的数据无法接收.

而StateFlow和SharedFlow是热流,创建的时候就已经在可以保存数据了.

简单来说 StateFlow和SharedFlow都可以监听数据的变化

类似LiveData的使用

下面直接上干货

准备工作:

复制代码
1
2
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:$vserion") implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:$vserion")

StateFlow简单使用

首先.需要在创建一个StateFlow

最简单的:

复制代码
1
val stateFlow: StateFlow<Int>

那们我们已经完成第一步,申明一个StateFlow的变量,他是Int型的.

接下来我们给他复制一个初始值,

这里要注意一下,StateFlow是必须要给初始值的,如果对象定义的是T?,可以赋值为null

复制代码
1
val stateFlow: StateFlow<Int> = MutableStateFlow(1)

这样 就完成了一个Int型stateFlow的创建.

其实这种写法是不好的,下面贴一下StateFlow的推荐创建方式

复制代码
1
2
val stateFlow: StateFlow<Int> get() = stateFlowImpl private val stateFlowImpl = MutableStateFlow(1)

为啥要这么写?后面会做讲解

接下来完成StateFlow的使用吧

其实没啥好说的 直接上代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//创建一个协程作用域 private val scope = CoroutineScope(Dispatchers.Default) //创建stateFlow对象 val stateFlow: StateFlow<Int> get() = stateFlowImpl private val stateFlowImpl = MutableStateFlow(1) private fun print(){ //监听stateFlow值的变化 scope.launch { stateFlow.collect { Log.d(TAG, "print: it = $it") } //切记 stateFlow的collect方法是个死循环, //所以下面任何代码都不会执行,想取消监听则取消这个job即可 } }

这样 我们就完成了对stateFlow数据变化监听

且看运行结果

复制代码
1
D/DaComing: print: it = 1

很明显,collect的时候就监听到了数据为1

即初始值

那如何对stateFlow进行数据的改变呢?

使用flow.emit方法即可

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//创建一个协程作用域 private val scope = CoroutineScope(Dispatchers.Default) //创建stateFlow对象 val stateFlow: StateFlow<Int> get() = stateFlowImpl private val stateFlowImpl = MutableStateFlow(1) private fun emitData(){ scope.launch { stateFlowImpl.emit(2) } } private fun print(){ //监听stateFlow值的变化 scope.launch { stateFlow.collect { Log.d(TAG, "print: it = $it") } //切记 stateFlow的collect方法是个死循环, //所以下面任何代码都不会执行,想取消监听则取消这个job即可 } }

这样 我们就完成了stateFlow数据的提交

然后我们执行代码

执行顺序是

复制代码
1
2
print() emitData()

看打印

复制代码
1
2
D/DaComing: print: it = 1 D/DaComing: print: it = 2

很简单,collect收到初始值1,然后emit 数值2后,监听到collect 2

当然你可以直接使用stateFlow里面的值

比如

复制代码
1
2
3
4
5
6
print() emitData() lifecycleScope.launchWhenResumed { Log.d(TAG, "value: ${stateFlow.value}") }

看结果

复制代码
1
2
3
D/DaComing: print: it = 1 D/DaComing: print: it = 2 D/DaComing: value: 2

这样,一旦上游有数据变化,下游就立马能收到数据监听,配合lifecycleScope既可以在activity活跃的时候collect数据,也可以在activity在后台的时候,进行数据变化.并且可以使用flow里面的值作为各种参数使用.功能比LiveData是要强大的.

以上就是StateFlow简单的使用

下面介绍下StateFlow的为什么叫StateFlow

StateFlow又称状态流,他只关心流的状态

举个栗子

不论上游发送了 emit 多少条数据 下游如果不处理完成,是不会收到collect的,

比如

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private fun print(){ scope.launch { stateFlow.collect { //处理一次状态要100ms Log.d(TAG, "print: it = $it") delay(100) } } } private fun emitData(){ scope.launch { for(i in 1..10){ //发送一次状态要20ms stateFlowImpl.emit(i) delay(20) } } }

执行顺序

复制代码
1
2
print() emitData()

运行结果

复制代码
1
2
3
D/DaComing: print: it = 1 D/DaComing: print: it = 5 D/DaComing: print: it = 10

我们把emit的地方称为上游,collect之后的处理称之为下游

首先 刚collect 的时候,收到初始值为1的回调,下游处理需要100ms

而上游每20ms发送一次数据,当第一次下游在处理的时候,上游已经发送了五条数据,1,2,3,4,5

这样,当下游处理完成的时候,才会继续collect当前最新的emit值,也就是stateFlow里面的value值

所以,不论上游提交了多少条数据,下游还没处理完成的时候,是不会进行多余emit数据的处理的.

好像这个原理类似于背压,不管上游压力有多啊,下游的处理速度都是一样的.

也不用担心多线程的问题了

并且,stateFlow如果上游emit了与上一次相同的数据,即使下游是空闲的,下游collect也不会收到数据.

最后,解答一下为什么要

    val stateFlow: StateFlow<Int> get() = stateFlowImpl
    private val stateFlowImpl = MutableStateFlow(1)

这么写的原因

其实我也不太懂哈哈哈

官方这么建议的 

不这么写也不会出错

但是 笔者个人的理解是

一般来说,StateFlow的下游的处理,都在view层,而在view层只负责对stateFlow的监听,

stateFlow可以collect而不能emit,只有stateFlowImpl可以emit数据

而StateFlow创建都是在数据处理层(用mvvm一半时viewmodel层),注意stateFlowImpl是private,

这样view层就无法直接参与数据的处理和交互,比较偏向于MVI?

MVI我也不太懂,但是感觉和MVVM大同小异,没有MVP和MVVM的差异大的感觉

那如果我每个事件都想要处理怎么办 StateFlow是会丢事件的啊

那么就要用上SharedFlow了.所以嘛,一般都是StateFlow是提交状态而非事件.下次再说

最后

以上就是暴躁季节最近收集整理的关于StateFlow与SharedFlow(一)的全部内容,更多相关StateFlow与SharedFlow(一)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部