一.StateFlow的设计
StateFlow是一种单数据更新的暖流,经过emit办法更新StateFlow的数据,经过value属性能够获取当时的数据。在StateFlow中,中心接口的承继联系如下图所示:
1.StateFlow接口
StateFlow接口承继自SharedFlow接口,代码如下:
public interface StateFlow<out T> : SharedFlow<T> {
// 当时的数据
public val value: T
}
-
订阅过程:在StateFlow中,每个FlowCollecter类型的目标都被称为订阅者。调用StateFlow类型目标的collect办法会触发订阅。正常情况下,订阅不会主动完毕,但订阅者能够取消订阅,当订阅者地点的协程被取消时,订阅过程就会取消。
-
冷流转化暖流:关于一个冷流,能够经过调用stateIn办法,转化为一个单数据更新的暖流。
-
持平判定:在StateFlow中,经过Any#equals办法来判别前后两个数据是否持平。当时后两个数据持平时,数据不会被更新,订阅者也不会处理。
-
数据缓存:StateFlow有必要要有一个初始值。当新订阅者出现时,StateFlow会将最新的数据发射给订阅者。StateFlow只保存最终发射的数据,除此之外不会缓存任何其他的数据。一起,StateFlow不支持resetReplayCache办法。
-
StateFlow并发: StateFlow中一切的办法都是线程安全的,而且能够在多协程并发的场景中运用且不用额定加锁。
-
操作符运用:对StateFlow运用flowOn操作符、conflate操作符、参数为CONFLATED或RENDEZVOUS的buffer操作符、cancellable操作符是无效的。
-
运用场景:运用StateFlow作为数据模型,能够表明任何状态。
-
StateFlow与SharedFlow的差异:StateFlow是SharedFlow的一种特定方向的、高性能的、高效的完成,广泛的用于单状态改变的场景,一切与SharedFlow相关基本规则、约束、操作符都适用于StateFlow。当运用如下的参数创立SharedFlow目标,并对其运用distinctUntilChanged操作符,能够得到一个与StateFlow行为相同的SharedFlow目标:
// StateFlow
val stateFlow = MutableStateFlow(initialValue)
// 与StateFlow行为相同的SharedFlow
// 留意参数
val sharedFlow = MutableSharedFlow(
replay = 1,
extraBufferCapacity = 0,
onBufferOverflow = BufferOverflow.DROP_OLDEST)
// 设置初始值
sharedFlow.tryEmit(initialValue)
// distinctUntilChanged办法,只有当时后发射的两个数据不一起才会将数据向下流发射
val state = sharedFlow.distinctUntilChanged()
-
StateFlow与ConflatedBroadcastChannel的差异:从概念上讲,StateFlow与ConflatedBroadcastChannel很类似,但二者也有很大的不同,引荐运用StateFlow,StateFlow设计的意图就是要在未来替代ConflatedBroadcastChannel:
- StateFlow更简单,不需求完成一堆与Channel相关的接口。
- StateFlow一直持有一个数据,而且无论在任何时间都能够安全的经过value属性获取。
- StateFlow清楚地划分了只读的StateFlow和可读可写的StateFlow。
- StateFlow对前后数据的比较是与distinctUntilChanged操作符类似的,而ConflatedBroadcastChannel对数据进行持平比较是基于标识引证。
- StateFlow不能关闭,也不能表明失利,因而如果需求,一切的错误与完成信号都应该具体化。
2. MutableStateFlow接口
MutableStateFlow接口承继自MutableSharedFlow接口与StateFlow接口,并在此基础上界说了一个新办法compareAndSet,代码如下:
public interface MutableStateFlow<T> : StateFlow<T>, MutableSharedFlow<T> {
// 当时数据
public override var value: T
// 经过CAS的方式,更新value
// 如果except与value持平,则将value更新为update,并回来true
// 如果except与value不持平,不做任何操作,直接回来false
// 如果except、value、update一起持平,不做任何操作,直接回来true
public fun compareAndSet(expect: T, update: T): Boolean
}
二.StateFlow的运用
1.MutableStateFlow办法
在协程中,能够经过调用MutableStateFlow办法创立一个MutableStateFlow接口指向的目标,代码如下:
public fun <T> MutableStateFlow(value: T): MutableStateFlow<T> {
...
}
经过MutableStateFlow办法能够创立一个类型为MutableStateFlow的目标,需求供给一个参数value,作为初始值。
在并发场景下调用emit办法时,会使StateFlow的数据快速更新,关于处理数据慢的订阅者,将会跳过这些快速更新的数据,但当订阅者需求处理数据时,获取的一定是最新更新的数据。
2.运用示例
代码如下:
private suspend fun test() {
// 创立一个暖流,初始值为1
val flow = MutableStateFlow(1)
// 将MutableStateFlow目标转化为StateFlow目标
// StateFlow目标不能调用emit办法,因而只能用于接纳
val onlyReadFlow = flow.asStateFlow()
// 接纳者1
// 发动一个新的协程
GlobalScope.launch {
// 触发并处理接纳的数据
onlyReadFlow.collect {
Log.d("liduozuishuai", "test1: $it")
}
}
// 接纳者2
// 发动一个新协程
GlobalScope.launch {
// 订阅监听,当collect办法触发订阅时,会首先会调onSubscription办法
onlyReadFlow.onSubscription {
Log.d("liduozuishuai", "test2: ")
// 发射数据:2
// 向下流发射数据:2,其他接纳者收不到
emit(2)
}.onEach {
// 处理接纳的数据
Log.d("liduozuishuai", "test2: $it")
}.collect()
}
// 发送数据:3,屡次发送
GlobalScope.launch {
flow.emit(3)
flow.emit(3)
flow.compareAndSet(3, 3)
}
}
关于上面的示例,接纳者1会依次打印出:1、3,接纳者2会依次打印出2、3。接纳者2由于在处理onSubscription办法发射的数据2时,MutableStateFlow目标内部的数据1变成了数据3,因而在处理完数据2后,直接处理数据3。