Kotlin協(xié)程之Flow觸發(fā)與消費(fèi)示例解析
示例
代碼如下:
launch(Dispatchers.Main) {
val task = flow {
emit(2)
emit(3)
}.onEach {
Log.d("liduo", "$it")
}
task.collect()
}一.Flow的觸發(fā)與消費(fèi)
在Kotlin協(xié)程:Flow基礎(chǔ)原理的分析中,流的觸發(fā)與消費(fèi)都是同時(shí)進(jìn)行的。每當(dāng)調(diào)用collect方法時(shí),會(huì)觸發(fā)流的執(zhí)行,并同時(shí)在collect方法中對(duì)流發(fā)出的值進(jìn)行消費(fèi)。
而在協(xié)程中,其實(shí)還提供了分離流的觸發(fā)與消費(fèi)的操作——onEach方法。通過(guò)使用onEach方法,可以將原本在collect方法中的消費(fèi)過(guò)程的移動(dòng)到onEach方法中。這樣在構(gòu)建好一個(gè)Flow對(duì)象后,不會(huì)立刻去執(zhí)行onEach方法,只有當(dāng)調(diào)用collect方法時(shí),才會(huì)真正的去觸發(fā)流的執(zhí)行。這樣就實(shí)現(xiàn)了流的觸發(fā)與消費(fèi)的分離。
接下來(lái),將對(duì)onEach方法進(jìn)行分析。
1.onEach方法
onEach方法用于預(yù)先構(gòu)建流的消費(fèi)過(guò)程,只有在觸發(fā)流的執(zhí)行后,才會(huì)對(duì)流進(jìn)行消費(fèi),代碼如下:
public fun <T> Flow<T>.onEach(action: suspend (T) -> Unit): Flow<T> = transform { value ->
action(value)
return@transform emit(value)
}
onEach方法是一個(gè)Flow接口的擴(kuò)展方法,返回一個(gè)類型為Flow的對(duì)象。Flow方法內(nèi)部通過(guò)transform方法實(shí)現(xiàn)。
2.transform方法
transform方法是onEach方法的核心實(shí)現(xiàn),代碼如下:
public inline fun <T, R> Flow<T>.transform(
@BuilderInference crossinline transform: suspend FlowCollector<R>.(value: T) -> Unit
): Flow<R> = flow { // 創(chuàng)建Flow對(duì)象
collect { value -> // 觸發(fā)collect
return@collect transform(value)
}
}
transform方法也是Flow接口的擴(kuò)展方法,同樣會(huì)返回一個(gè)類型為Flow的對(duì)象。并且在transform方法內(nèi)部,首先構(gòu)建了一個(gè)類型為Flow的對(duì)象,并且在這個(gè)Flow對(duì)象的執(zhí)行體內(nèi),調(diào)用上游的流的collect來(lái)觸發(fā)消費(fèi)過(guò)程,并通過(guò)調(diào)用參數(shù)transform來(lái)實(shí)現(xiàn)消費(fèi)。這個(gè)collect方法是一個(gè)擴(kuò)展方法,在Kotlin協(xié)程:Flow基礎(chǔ)原理分析過(guò),因此不再贅述。
這就是onEach方法實(shí)現(xiàn)觸發(fā)與消費(fèi)分離的核心,它將對(duì)上游的流的消費(fèi)過(guò)程包裹在了一個(gè)新的流內(nèi),只有當(dāng)這個(gè)新的流或其下游的流被觸發(fā)時(shí),才會(huì)觸發(fā)這個(gè)新的流自身的執(zhí)行,從而實(shí)現(xiàn)對(duì)上游的流的消費(fèi)。
接下來(lái)分析一下流的消費(fèi)過(guò)程。
3.collect方法
collect方法用于觸發(fā)流的消費(fèi),我們這里調(diào)用的collect方法,是一個(gè)無(wú)參數(shù)的方法,代碼如下:
public suspend fun Flow<*>.collect(): Unit = collect(NopCollector)
這里的無(wú)參數(shù)collect方法是Flow接口的擴(kuò)展方法。在無(wú)參數(shù)collect方法中,調(diào)用了另一個(gè)有參數(shù)的collect方法,這個(gè)有參數(shù)的collect方法在Kotlin協(xié)程:Flow基礎(chǔ)原理中提到過(guò),就是Flow接口中定義的方法,并且傳入了NopCollecor對(duì)象,代碼如下:
internal object NopCollector : FlowCollector<Any?> {
override suspend fun emit(value: Any?) {
// 什么都不做
}
}
NopCollecor是一個(gè)單例類,它實(shí)現(xiàn)了FlowCollector接口,但是emit方法為空實(shí)現(xiàn)。
因此,這里會(huì)調(diào)用onEach方法返回的Flow對(duì)象的collect方法,這部分在Kotlin協(xié)程:Flow基礎(chǔ)原理進(jìn)行過(guò)分析,最后會(huì)觸發(fā)flow方法中的block參數(shù)的執(zhí)行。而這個(gè)Flow對(duì)象就是transform方法返回的Flow對(duì)象。代碼如下:
public inline fun <T, R> Flow<T>.transform(
@BuilderInference crossinline transform: suspend FlowCollector<R>.(value: T) -> Unit
): Flow<R> = flow { // 創(chuàng)建Flow對(duì)象
collect { value -> // 觸發(fā)collect
return@collect transform(value)
}
}
通過(guò)上面的transform方法可以知道,在觸發(fā)flow方法中的block參數(shù)執(zhí)行后,會(huì)調(diào)用collect方法。上面提到transform方法是Flow接口的擴(kuò)展方法,因此這里有會(huì)繼續(xù)調(diào)用上游Flow對(duì)象的collect方法。這個(gè)過(guò)程與剛才分析的類似,這里調(diào)用的上游的Flow對(duì)象,就是我們?cè)谑纠a中通過(guò)flow方法構(gòu)建的Flow對(duì)象。
此時(shí),會(huì)觸發(fā)上游flow方法中block參數(shù)的執(zhí)行,并在執(zhí)行過(guò)程中,通過(guò)emit方法將值發(fā)送到下游。
接下來(lái),在transform方法中,collect方法的block參數(shù)會(huì)被會(huì)被回調(diào)執(zhí)行,處理上游發(fā)送的值。這里又會(huì)繼續(xù)調(diào)用transform方法中參數(shù)的執(zhí)行,這部分邏輯在onEach方法中,代碼如下:
public fun <T> Flow<T>.onEach(action: suspend (T) -> Unit): Flow<T> = transform { value ->
action(value)
return@transform emit(value)
}
這里會(huì)調(diào)用參數(shù)action的執(zhí)行,流在這里最終被消費(fèi)。同時(shí),onEach方法會(huì)繼續(xù)調(diào)用emit方法,將上游返回的值再原封不動(dòng)的傳遞到下游,交由下游的流處理。
二.多消費(fèi)過(guò)程的執(zhí)行
首先看下面這段代碼:
launch(Dispatchers.Main) {
val task = flow {
emit(2)
emit(3)
}.onEach {
Log.d("liduo1", "$it")
}.onEach {
Log.d("liduo2", "$it")
}
task.collect()
}
根據(jù)上面的分析,兩個(gè)onEach方法會(huì)按順序依次執(zhí)行,打印出liduo1:2、liduo2:2、liduo1:3、liduo2:3。就是因?yàn)閛nEach方法會(huì)將上游的值繼續(xù)向下游發(fā)送。
同樣的,還有下面這段代碼:
launch(Dispatchers.Main) {
val task = flow {
emit(2)
emit(3)
}.onEach {
Log.d("liduo1", "$it")
}
task.collect {
Log.d("liduo2", "$it")
}
}
這段代碼也會(huì)打印出liduo1:2、liduo2:2、liduo1:3、liduo2:3。雖然使用了onEach方法,但也可以調(diào)用有參數(shù)的collect方法來(lái)對(duì)上游發(fā)送的數(shù)據(jù)進(jìn)行最終的處理。
三.總結(jié)

粉線為代碼編寫順序,綠線為下游觸發(fā)上游的調(diào)用順序,紅線為上游向下游發(fā)送值的調(diào)用順序,藍(lán)線為onEach方法實(shí)現(xiàn)的核心。
到此這篇關(guān)于Kotlin協(xié)程之Flow觸發(fā)與消費(fèi)示例解析的文章就介紹到這了,更多相關(guān)Kotlin Flow觸發(fā)與消費(fèi)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Kotlin by lazy關(guān)鍵字深入探究實(shí)現(xiàn)原理
這篇文章主要介紹了by lazy,在kotlin中使用是很常見(jiàn)的,用于實(shí)現(xiàn)懶加載某個(gè)數(shù)據(jù)。而這兩個(gè)單詞不是一體的,其中by是kotlin中的關(guān)鍵字,用于實(shí)現(xiàn)委托;lazy是一個(gè)方法,他的返回值是委托的具體對(duì)象2022-11-11
Flutter?Ping檢查服務(wù)器通訊信號(hào)強(qiáng)度實(shí)現(xiàn)步驟
這篇文章主要為大家介紹了Flutter?Ping檢查服務(wù)器通訊信號(hào)強(qiáng)度實(shí)現(xiàn)步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Android使用個(gè)推實(shí)現(xiàn)三方應(yīng)用的推送功能
這篇文章主要為大家詳細(xì)介紹了Android使用個(gè)推實(shí)現(xiàn)三方應(yīng)用的推送功能,感興趣的小伙伴們可以參考一下2016-08-08
Android EditText監(jiān)聽(tīng)回車鍵并處理兩次回調(diào)問(wèn)題
這篇文章主要介紹了Android EditText監(jiān)聽(tīng)回車鍵并處理兩次回調(diào)問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
獲取Android系統(tǒng)唯一識(shí)別碼的方法
這篇文章主要介紹了獲取Android系統(tǒng)唯一識(shí)別碼的方法,涉及通過(guò)編程獲取Android系統(tǒng)硬件設(shè)備標(biāo)識(shí)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
使用RecyclerView實(shí)現(xiàn)水平列表
這篇文章主要為大家詳細(xì)介紹了使用RecyclerView實(shí)現(xiàn)水平列表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
Android頂部工具欄和底部工具欄的簡(jiǎn)單實(shí)現(xiàn)代碼
Android頂部工具欄和底部工具欄的簡(jiǎn)單實(shí)現(xiàn)代碼,需要的朋友可以參考一下2013-05-05

