协程中的StateFlow与ShareFlow
流的冷热之分
使用过RxJava
的同学都知道,在RxJava
中流存在冷流和热流这么一种说法的,那么冷流和热流有什么区别呢?
- 冷流:在流未进行收集或者订阅的时候,整条操作链是不会运行,并且不会向接收者推送数据。订阅者之间也不会存在值共享
- 热流:在订阅或者收集之前值就会产生,并且当有新的订阅者时,他会接收到订阅之后流中所发送的所有值,存在值共享
在Flow
中也会存在冷流、热流之分,今天介绍的StateFlow
与ShareFlow
就是属于热流,那么就进入今天的正文
StateFlow
StateFloW
是一个可观察的状态容器,我们可以通过value
属性进行状态的更新和状态的当前状态值的读取,这一点是不是和我们平常使用flow
的时候不一样呢,在冷流中我们是使用emit()
,和构建流的同时来进行值的发射
StateFlow的创建
1、使用构造方法进行创建,并且设定一个初始值
val state = MutableStateFlow<String>("default")
2、状态的更新
state.value = "pushNewValue"
3、状态收集(collect是suspend函数,所以收集状态需要在协程作用域范围内)
GlobalScope.launch(Dispatchers.IO){
state.collect{
Log.d("StateFlow",it)
}
}
StateFlow使用示例
下面通过一个示例来演示以下
class loginviewModel(){
val loginSuccess = MutableStateFlow<Boolean>(false)
fun login(){
lifecycleScope.launch{
repository.login()
.loading()
.catch()
.collect{
loginSuccess.value = true
}
}
}
}
HomeActvity.class
class LoginActivity : AppCompatActivity(){
override fun onCreate(){
...
lifecycleScope.launch{
viewModel.loginSuccess.collect{
//UI逻辑
....
}
}
}
}
上面示例会发现,这种开发模式在我们使用LiveData
时具有相同的地方,两者都是可观察的数据容器,所以坊间传闻StateFlow
会替代LiveData
,我们了解下二者有何区别,你就会得到答案
-
StateFlow
在构建时需要给定初始状态值,LiveData
是不需要初始状态的 - 如果当Activity进入后台时,
Livedata
会自动帮我们把使用方取消注册,但是StateFlow
并没有关联生命周期,所以不会停止状态的更新推送,如果需要实现一样的功能,官方推荐我们在lifeCycle.repeatOnLifeCycle
中进行数据的收集 ,也可以在生命周期函数中将Job.cancel()
SharedFlow
ShareFlow
和StateFlow
类似,都是热流,但是SharedFlow
更为灵活,但是区别在于SharedFLOW
并不支持将旧值发送给新的订阅者,并且可以缓存策略,接下来我们看下怎么使用
SharedFlow的创建
val connectState = MutableSharedFlow<Boolean>(0,0,BufferOverflow.SUSPEND)
public fun <T> MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T> {
...
}
上面为MutableSharedFlow
的创建源码,需要了解他的三个配置参数
-
replay :当新的订阅者订阅需要重新将多少旧值重放给它
-
extraBufferCapacity
:除开replay之外的缓冲区大小 -
onBufferOverflow
:缓冲区溢出策略SUSPEND:缓冲区满时就会被挂起 DROP_OLDEST: 缓冲区满时就会删除最旧的值 DROP_LATEST:删除即将进入缓冲区的最新值
使用emit
或者tryEmit
进行值的发送
connectState.tryEmit(false)
connectState.emit(false)
tryEmit
会将发射的结果回调,并且如果缓冲区策略配置为suspend时会将这次数据的发射挂起,知道缓冲区有空间时再进行发射。
emit
当缓冲区没有空间时,该操作就会挂起
SharedFlow的收集
GlobalScope.launch(Dispatchers.IO){
connectstate.collect{
...
}
}
冷流转换成热流
官方分别提供stateIn
使Flow转换成StateFlow
以及使用shareIn
操作符转换为SharedFlow
class HomeViewModel(){
val homeState:Flow<ViewState> = flowof{
ViewState.Loading
}.stateIn(coroutineScope)
}
使用shareIn
进行转换需要了解它的参数配置
public fun <T> Flow<T>.shareIn(
scope: CoroutineScope,
started: SharingStarted,
replay: Int = 0
)
- scope: sharedFlow的启动作用域
- started:启动策略
-
SharingStarted.WhileSubscribed()
如果存在数据收集者,上游数据提供方保持活跃状态 -
SharingStarted.Eagerly
立即启动数据提供方 -
SharingStarted.Lazily
存在数据收集者开始提供数据,并且永远保持活跃状态
-