拿一个去重的操作符*distinctUntilChanged
*来分析
val numbers = flowOf(1, 1, 2, 2, 3, 4, 4, 4, 5)
numbers.distinctUntilChanged.collect {
println(it)
}
构造单个Flow
先生成一个Flow
,这个Flow
主要是循环发送数据
public fun <T> flowOf(vararg elements: T): Flow<T> = flow {
for (element in elements) {
emit(element)
}
}
Flow
是冷流,意思就是只有你调用collect
方法它才触发发送emit
数据的方法,为啥调用collect
方法会触发数据?
看一下最终构造Flow
对象的方法
internal inline fun <T> unsafeFlow(@BuilderInference crossinline block: suspend FlowCollector<T>.() -> Unit): Flow<T> {
return object : Flow<T> {
override suspend fun collect(collector: FlowCollector<T>) {
collector.block()
}
}
}
Flow
的关键方法是collect
,在调用这个方法的时候,才去执行,最初的代码块collector.block()
也就是demo
中这段代码
{
for (element in elements) {
emit(element)
}
}
当我们调用collect
方法之后,会触发上游发送方法emit
,这里的demo
上游是循环发送,每次发送一个数据。当上游调用发送方法之后,最后会在调用collect
方法时候,传入的FlowCollector.emit
实现类中接收到
collect {
println(it)
}
多个Flow链式操作
先来看一个去重复的Flow
操作符distinctUntilChanged
val numbers = flowOf(1, 1, 2, 2, 3, 4, 4, 4, 5)
numbers.distinctUntilChanged().collect {
println(it)
}
先来简单看一下关键代码,这里我们不需要关注去重的逻辑如何实现的,只需要关注两个Flow之间,如何链式关联发送数据的,理解他们之前的如何关联底层关键逻辑,去重只是这个操作符具体业务的逻辑,这里我会去掉一些代码
public fun <T> Flow<T>.distinctUntilChanged(): Flow<T> =
distinctUntilChangedBy(
keySelector = defaultKeySelector,
areEquivalent = defaultAreEquivalent
)
private fun <T> Flow<T>.distinctUntilChangedBy(
keySelector: (T) -> Any?,
areEquivalent: (old: Any?, new: Any?) -> Boolean
): Flow<T> = when {
...
else -> DistinctFlowImpl(this, keySelector, areEquivalent)
}
private class DistinctFlowImpl<T>(
private val upstream: Flow<T>,
@JvmField val keySelector: (T) -> Any?,
@JvmField val areEquivalent: (old: Any?, new: Any?) -> Boolean
): Flow<T> {
override suspend fun collect(collector: FlowCollector<T>) {
var previousKey: Any? = NULL
upstream.collect { value ->
val key = keySelector(value)
@Suppress("UNCHECKED_CAST")
if (previousKey === NULL || !areEquivalent(previousKey, key)) {
previousKey = key
collector.emit(value)
}
}
}
}
主要看一下DistinctFlowImpl
中collect
方法的实现,DistinctFlowImpl
内部持有了上一个Flow
,
当DistinctFlowImpl
Flow
调用collect
方法的时候,开始触发upstream.collect
上游Flow
的collect
方法,前面聊过,当触发一个Flow
的collect
方法的时候,才是发送数据的开始,所以
会先执行下面这段发送逻辑,也就是上游的代码块block
{
for (element in elements) {
emit(element)
}
}
上游发送数据之后,上游collect
方法开始收到数据
upstream.collect { value ->
//上游开始收到数据
}
然后由于下游持有上游的实例,这时候开始由下游发送数据
collector.emit(value)
下游发送数据之后,下游的collect
方法也开始收到数据,这样数据就从上到下流动起来了。多个流之前链式操作。
numbers.distinctUntilChanged().collect {
println(it)//下游开始收到数据
}
总结
- 多个流之前的操作,基本就是下游持有上游对象,在下游开始
collect
方法的时候,回去触发上游的collect
,上游发送数据emit
之后,上游收到collect
数据,然后继续到下游发送emit数据,最后到下游收到collect
数据。 - 一般操作符都会生成一个新的
Flow
,新的Flow
会持有上一个关联的Flow
,一般我们称为上游。