参考文章:https://developer.android.com/kotlin/flow?hl=zh-cn
涉及到的源码版本(不同版本实现可能有出入):
文章目录
flow() 方法
以下内容,都是基于 flow()
顶级方法的内部实现是基于 SafeCollector
和 SafeFlow
来说明的。
实现原理
public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)
接收一个 FlowCollector<T>.() -> Unit
类型的 suspendable lambda 表达式,返回 SafeFlow
,使用示例:
private val block: (suspend FlowCollector<Int>.() -> Unit) = {
val random = Random(9)
while (true) {
// 调用 FlowCollector.emit() 发射数据
emit(random.nextInt())
}
}
private val testFlow: Flow<Int> = flow<Int>(block)
而返回的 SafeFlow
实现了 interface Flow<out T>
。注意,并不是通过 Flow 来发射数据,而是由 lamdba 回调传入的 FlowCollector
实例来发射数据。
viewModelScope.launch {
val collector = object : FlowCollector<Int> {
override suspend fun emit(value: Int) {
// do something
}
}
testFlow.collect(collector)
}
private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : Flow<T> {
override suspend fun collect(collector: FlowCollector<T>) {
// 先创建 SafeCollector 实例,然后再执行 block lambda
SafeCollector(collector, coroutineContext).block()
}
}
对于 SafeFlow
,会在 collect()
被调用时实例化出 SafeCollector
对象,并执行最开始传入的 FlowCollector<T>.() -> Unit
类型的 block lambda。即执行前面提到的这段逻辑,
val random = Random(9)
while (true) {
// 调用 FlowCollector.emit() 发射数据
// 伪代码
this.SafeCollector.emit(random.nextInt())
}
因此在调用 this.SafeCollector.emit(random.nextInt())
的时候,实际上是触发testFlow.collect(collector)
传入的 collector.emit()
回调方法:
internal class SafeCollector<T>(
private val collector: FlowCollector<T>,
private val collectContext: CoroutineContext
) : FlowCollector<T> {
override suspend fun emit(value: T) {
...
collector.emit(value)
}
...
}
所以简单的说,在 flow(){}
中通过 FlowCollector.emit()
发射数据,实际上会触发 Flow.collect()
时传入的 FlowCollector.emit()
回调方法,注意,两个 FlowCollector
不是直接的同一对象。
且是在调用 Flow.collect()
的时候才会触发 block lambda 的执行。
关于线程切换
viewModelScope.launch {
val collector = object : FlowCollector<Int> {
override suspend fun emit(value: Int) {
// do something
// 所处线程由外层的协程决定,即 viewModelScope.launch() 决定
}
}
testFlow
.map {}
.flowOn(Dispatchers.IO) // 第一个 flowOn
.map {}
.flowOn(Dispatchers.Main)
.collect(collector)
}
private val block: (suspend FlowCollector<Int>.() -> Unit) = {
// 所处线程由 Flow<T>.flowOn() 决定
val random = Random(9)
while (true) {
// 调用 FlowCollector.emit() 发射数据
emit(random.nextInt())
}
}
private val testFlow: Flow<Int> = flow<Int>(block)
- block suspendable lambda 所处的线程,是由链式表达式中的第一个
flowOn()
来决定的, collect(collector)
的collector.emit()
回调所处线程(即消费数据时所处的线程),是由当前协程指定的线程决定的。
补充说明
通过 flow<Int>(block)
得到的 SafeFlow 对象,是可以被多次 collect()
的,只不过需要注意,FlowCollector#emit()
是 suspendable 的,所以在同一协程作用域下的一段逻辑的执行进度,会受到 block lambda 内逻辑执行进度的影响。
比如有如下逻辑:
private val block: (suspend FlowCollector<Int>.() -> Unit) = {
val random = Random(9)
val nextInt = random.nextInt()
this.emit(nextInt)
// 定义一个死循环
while (true) { }
}
viewModelScope.launch {
Log.d("TAG", "${Thread.currentThread()}, start ++++++++++++++")
val collector = object : FlowCollector<Int> {
override suspend fun emit(value: Int) {
// do thing
}
}
testFlow
.flowOn(Dispatchers.IO)
.collect(collector)
Log.d("TAG", "${Thread.currentThread()}, end ++++++++++++++")
}
由于在 block lambda 中代码段末尾,定义了一个死循环,即这段逻辑一直不会结束,因而在 viewModelScope.launch {}
代码段中的最后一行(打印 end 日志的逻辑)也会一直得不到执行。
StateFlow 与 SharedFlow
StateFlow
StateFlow
是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新。还可通过其value
属性读取当前状态值(与SharedFlow
的主要区别)。如需更新状态并将其发送到数据流,请为MutableStateFlow
类的value
属性分配一个新值。
// 实际上是一个接口,定义了一个 value 字段表示当前状态值。
public interface StateFlow<out T> : SharedFlow<T> {
/**
* The current value of this state flow.
*/
public val value: T
}
可以通过顶级方法 MutableStateFlow()
来获取一个 value
可写类型的 StateFlow
:
public fun <T> MutableStateFlow(value: T): MutableStateFlow<T> = StateFlowImpl(value ?: NULL)
其实现类型则是 StateFlowImpl
,即 MutableStateFlow
类型。
需要注意的是,在 View
中订阅监听的时候,需要注意生命周期的问题,如下示例:
// 在 ViewModel 中定义
private val _uiState = MutableStateFlow<Int>(0)
val uiState: StateFlow<Int> = _uiState
// 在 Activity 中使用
// (1)
lifecycleScope.launchWhenStarted {
viewModel.uiState.collect {
// do something
}
}
//(2)
lifecycleScope.launch {
// repeatOnLifecycle launches the block in a new coroutine every time the
// lifecycle is in the STARTED state (or above) and cancels it when it's STOPPED.
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Trigger the flow and start listening for values.
// Note that this happens when lifecycle is STARTED and stops
// collecting when the lifecycle is STOPPED
viewModel.uiState.collect {
}
}
}
因为不会像 LiveData
那样在监听的时候直接关联 LifecycleOwner
,在使用的时候需要注意基于 LifecycleCoroutineScope#launchWhenXXX()
或者 LifecycleOwner#repeatOnLifecycle()
来保证与 flow 相关的逻辑生命周期可控。
注意:repeatOnLifecycle API 仅在
androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha01
库及更高版本中提供。
与 LiveData 比较
用法类似,用途也基本上都是做可观察的数据容器类。但是也有些区别:
LiveData
在订阅监听的时候,会基于LifecycleOwner
来保证逻辑生命周期可控,而StateFlow
需要借助如上的方法;StateFlow
在初始化的时候必须赋予初始值。
SharedFlow
可以通过 MutableSharedFlow()
方法获取可变类型的 SharedFlow
实例。
public fun <T> MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T> {}
Creates a MutableSharedFlow with the given configuration parameters.
This function throws IllegalArgumentException on unsupported values of parameters or combinations thereof.
Params:
replay - the number of values replayed to new subscribers (cannot be negative, defaults to zero).
extraBufferCapacity - the number of values buffered in addition to replay. emit does not suspend while there is a buffer space remaining (optional, cannot be negative, defaults to zero).
onBufferOverflow - configures an action on buffer overflow (optional, defaults to suspending attempts to emit a value, supported only when replay > 0 or extraBufferCapacity > 0).
- replay,为新的订阅者发送包含最新的指定 replay 个数的值,只能是非负数,默认 0;
- extraBufferCapacity,除了 replay 数量之外的缓冲容量,在达到容量之前,emit() 不会挂起,默认为 0;
- onBufferOverflow,缓冲溢出时的可选操作(只有在
replay>0
orextraBufferCapacity>0
时有效),默认挂起emit()
,可选值有SUSPEND
、DROP_LATEST
、DROP_OLDEST
。
另外,还包含 subscriptionCount
属性用于获取有效的 collecter
数量:
/**
* The number of subscribers (active collectors) to this shared flow.
*
* The integer in the resulting [StateFlow] is not negative and starts with zero for a freshly created
* shared flow.
*
* This state can be used to react to changes in the number of subscriptions to this shared flow.
* For example, if you need to call `onActive` when the first subscriber appears and `onInactive`
* when the last one disappears, you can set it up like this:
*
* ```
* sharedFlow.subscriptionCount
* .map { count -> count > 0 } // map count into active/inactive flag
* .distinctUntilChanged() // only react to true<->false changes
* .onEach { isActive -> // configure an action
* if (isActive) onActive() else onInactive()
* }
* .launchIn(scope) // launch it
* ```
*/
public val subscriptionCount: StateFlow<Int>
相比于 StateFlow
的使用场景:
- 发生订阅时,需要将过去已经更新的 n 个值,同步给新的订阅者;
- 配置缓存策略。
冷/热数据流
热数据流(hot stream),没有消费者的时候也可以生产数据,如 StateFlow
、SharedFlow
。
冷数据流(cold stream),需要在 collect()
之后才生产值,即无消费者的时候不会生产数据,比如前面的 flow()
方法涉及到的使用。
Note: Cold flows are created on-demand and emit data when they’re being observed. Hot flows are always active and can emit data regardless of whether or not they’re being observed.
from https://medium.com/androiddevelopers/things-to-know-about-flows-sharein-and-statein-operators-20e6ccb2bc74
Flow.shareIn 与 Flow.stateIn
参考文章:https://medium.com/androiddevelopers/things-to-know-about-flows-sharein-and-statein-operators-20e6ccb2bc74
用途
两者都是用于将 cold stream 转换为 hot stream,shareIn
返回 SharedFlow
,stateIn
返回 StateFlow
。
目的
- 提高性能:
- cold stream 是每次在被 observe(即被 collect)的时候,才会生成生产内容,这表示每次被 observe 的时候,每一个 collector 获取到的都是新的数据实例,同时产生新的中间对象(注意不是说产生不同的 Flow 对象,就像前面提到的
flow()
定级方法,得到的是 cold stream 类型的 Flow 对象,而该 Flow 对象每次被 collect 的时候,都会对应生产新的FlowController
实例,即中间对象,用于针对每个不同的 collector 来生产内容); - 而 hot stream,每次被 observe (即被 collect)的时候,所有的 collector 都是共享的相同的数据实例。
- cold stream 是每次在被 observe(即被 collect)的时候,才会生成生产内容,这表示每次被 observe 的时候,每一个 collector 获取到的都是新的数据实例,同时产生新的中间对象(注意不是说产生不同的 Flow 对象,就像前面提到的
- 实现 Buffering Events,通过 shareIn 来转换为 SharedFlow,从而通过其replay 参数,来缓冲指定个数的之前被 emit 的内容,使得新的 collector 在 obserbe 的时候总是能获取到之前被缓冲的内容;
- 实现 Caching Data,通过 stateIn 来转换为 StateFlow,实现为新的 collector 缓存此前最后一次被 emit 的内容。