自上而下,關(guān)注整體思路,文章不考慮更多細(xì)節(jié)
//flow示例
CoroutineScope(Dispatchers.Default).launch {
flow {
//這部分就是collector
(1..a).forEach{
delay(1000)
emit(it + 1)
}
}.collect {
Log.e("flow collect"," $it")
}
}
拆解
class FlowCollectorImpl<Int>: FlowCollector<Int> {
suspend fun block(){
(1..a).forEach {
// 每隔 1 秒發(fā)射 1 個(gè)數(shù)字
delay(1000)
emit(it + 1)
}
}
override suspend fun emit(value: Int) {
Log.e("flow collect"," $it")// 打印數(shù)字
}
}
//源碼中的內(nèi)容示意
private class Flow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
override suspend fun collect(collector: FlowCollector<T>) {
collector.block() //引擎點(diǎn)火!
}
}
如果之前不太明白這點(diǎn)的通過(guò)上面拆解代碼應(yīng)該可以有所得
部分源碼
public fun interface FlowCollector<in T> {
public suspend fun emit(value: T)
}
public fun <T> flow(
@BuilderInference block: suspend FlowCollector<T>.() -> Unit
): Flow<T> = SafeFlow(block)
private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : kotlinx.coroutines.flow.Flow<T>,
CancellableFlow<T> {
public final override suspend fun collect(collector: FlowCollector<T>) {
val safeCollector = SafeCollector(collector, coroutineContext)
try {
collectSafely(safeCollector)
} finally {
safeCollector.releaseIntercepted()
}
}
override suspend fun collectSafely(collector: FlowCollector<T>) {
collector.block()
//相當(dāng)于調(diào)用上面拆解的block(),block()中調(diào)用了emit(),開始發(fā)射并收集數(shù)據(jù)
}
}
flow{}后面的lambda其實(shí)相當(dāng)于就是FlowCollector的一個(gè)擴(kuò)展函數(shù),
collect{ }后面的lambda就是emit方法的實(shí)現(xiàn)。
調(diào)用collect之后,會(huì)調(diào)用collector.block(),而block()調(diào)用了emit(),從而開始打印數(shù)據(jù)。
Flow的filter、map等方法原理和關(guān)鍵源碼:
核心原理:過(guò)濾本質(zhì)是依次收集原flow數(shù)據(jù)后按設(shè)定條件過(guò)濾或轉(zhuǎn)換后生成新的flow對(duì)象,發(fā)射新的轉(zhuǎn)換后的數(shù)據(jù)!
filter和map方法都是使用transform()實(shí)現(xiàn),而transform()其實(shí)就是接收了上游數(shù)據(jù)后,再生成新的flow發(fā)給下游。
public inline fun <T> Flow<T>.filter(crossinline predicate: suspend (T) -> Boolean): Flow<T> = transform { value ->
if (predicate(value)) return@transform emit(value)
}
public inline fun <T, R> Flow<T>.transform(
@BuilderInference crossinline transform: suspend FlowCollector<R>.(value: T) -> Unit
): Flow<R> = flow { // Note: safe flow is used here, because collector is exposed to transform on each operation
collect { value ->
// kludge, without it Unit will be returned and TCE won't kick in, KT-28938
return@collect transform(value)
}
}
上面的源碼中,會(huì)生成一個(gè)新的Flow對(duì)象,而該Flow對(duì)象的數(shù)據(jù)源(收集者)就是從原Flow數(shù)據(jù)中collect的數(shù)據(jù)。然后經(jīng)過(guò)轉(zhuǎn)換,再次發(fā)射出去,而這個(gè)新Flow就是下游即將接收的經(jīng)過(guò)篩選或轉(zhuǎn)換后的新數(shù)據(jù)流。
看上面源碼中的其中部分
collect { value ->
// kludge, without it Unit will be returned and TCE won't kick in, KT-28938
return@collect transform(value)
}
這部分就是收集原flow中的數(shù)據(jù),然后按轉(zhuǎn)換條件去發(fā)射新的數(shù)據(jù)。(transform方法中會(huì)執(zhí)行emit(value) )。
冷流的設(shè)計(jì)核心部分就是依靠函數(shù)參數(shù)
這一特性來(lái)實(shí)現(xiàn)的(函數(shù)參數(shù)大多數(shù)情況為lambda表達(dá)式),函數(shù)參數(shù)是kotlin最具創(chuàng)造性和顛覆性的特性之一,也是源碼中很多復(fù)雜方法看起來(lái)難以理解的原因之一。函數(shù)參數(shù)就是一個(gè)死參數(shù),開發(fā)者可以直接執(zhí)行它(),或者不去執(zhí)行而是繼續(xù)向下傳遞該函數(shù)參數(shù),如果你不主動(dòng)去調(diào)用它(),它是不會(huì)執(zhí)行它方法體內(nèi)的代碼的。比如上面案例源碼:
flow { // Note: safe flow is used here, because collector is exposed to transform on each operation
collect { value ->
// kludge, without it Unit will be returned and TCE won't kick in, KT-28938
return@collect transform(value)
}
}
生成新的flow時(shí),雖然其內(nèi)部調(diào)用了collect{},但是其并沒(méi)有真正執(zhí)行(它重寫了emit()),只是作為一個(gè)參數(shù)繼續(xù)在flow構(gòu)造內(nèi)部向下傳遞該參數(shù),最后在
private class SafeFlow<T>(private val block: suspend FlowCollector<T>.() -> Unit) : AbstractFlow<T>() {
override suspend fun collectSafely(collector: FlowCollector<T>) {
collector.block()
}
}
public final override suspend fun collect(collector: FlowCollector<T>) {
val safeCollector = SafeCollector(collector, coroutineContext)
try {
collectSafely(safeCollector)
} finally {
safeCollector.releaseIntercepted()
}
}
上面中的collectSafely()中才真正被執(zhí)行,上面是使用函數(shù)參數(shù)(FlowCollector的擴(kuò)展函數(shù),這個(gè)擴(kuò)展函數(shù)就是生成flow{}時(shí)其內(nèi)的lambda表達(dá)式)重寫了collectSafely()!而collectSafely()是在collect()中執(zhí)行的,也就是開始收集時(shí)才執(zhí)行的,這就形成了閉環(huán)。這也是冷流的‘冷’原因所在,前期的工作是在“造車”,最后的執(zhí)行才“點(diǎn)火啟動(dòng)”。
這一特性,使得kotlin擁有了無(wú)與倫比的功能構(gòu)造能力。方法可以先通過(guò)傳遞函數(shù)參數(shù)來(lái)構(gòu)造功能的整體架構(gòu),構(gòu)造完成之后最后去執(zhí)行來(lái)實(shí)現(xiàn)整個(gè)功能的運(yùn)轉(zhuǎn)。
如有問(wèn)題,歡迎各位指正,感謝