第一步
首先回顧下前面的知識(shí)點(diǎn):
flow提供的只是一個(gè) 「擴(kuò)展函數(shù)」 返回的是一個(gè)保存了這個(gè)方法的類實(shí)例,并且該類提供emit方法以供flow中調(diào)用
構(gòu)建Flow
「flow方法」
object Flow {
fun flow(collect: Collector<T>.() -> Unit): SafeFlowCollector {
return SafeFlowCollector(collect)
}
}
定義一個(gè)Flow類,內(nèi)部提供flow方法。
「SafeCollector」 類:
class SafeFlowCollector<T>(val collect: Collector.() -> Unit) {
//將該Function保存在調(diào)用flow后創(chuàng)建的實(shí)例中獲取實(shí)例創(chuàng)建FlowCollector
fun collectFunction(a: (T) -> Unit) {
val co = Collector(a)
co.collect()
}
「Collector類」
class Collector<T>(val action: (a: T) -> Unit) {
fun emit(value: T) {
action(value)
}
}
這是flow方法需要?jiǎng)?chuàng)建的三個(gè)類,雖然功能不多,但是對(duì)于簡(jiǎn)單的構(gòu)建流還是綽綽有余的。
分析
可以看到flow方法傳入的方法參數(shù)collect被定義為了Collector的擴(kuò)展函數(shù),并且保存在了剛創(chuàng)建的SafeCollector的類中用collect函數(shù)表示。
「第一個(gè)功能」 :flow參數(shù)提交的類型和collect中收到的類型一致,我采用了更加直接的形式定義flow時(shí)需要設(shè)置傳輸?shù)念愋停趀mit和collect中都是對(duì)應(yīng)的類型。
「實(shí)現(xiàn)」
flow定義類型和emit類型保持一致:通過(guò)Collector< T >實(shí)現(xiàn)
flow定義類型和收集到的類型一致:通過(guò)SafeFlowCollector< T >實(shí)現(xiàn)
第二步
構(gòu)建collect收集器
第一步發(fā)射器設(shè)置好后,我們限制了發(fā)送的類型和接受的類型,并且將發(fā)送邏輯保存在了實(shí)例中。接下來(lái)我們需要調(diào)用該實(shí)例對(duì)象以觸發(fā)發(fā)送邏輯,在發(fā)送邏輯中還需要調(diào)用到我們收集的邏輯。
因此收集邏輯需要單獨(dú)存放,因此需要單獨(dú)構(gòu)建一個(gè)類,這個(gè)類還必須可以調(diào)用到發(fā)送邏輯。
?注:Flow中采用的是collect收集觸發(fā)flow流發(fā)送邏輯,本人使用時(shí)是按照collectFunction定義的。兩者邏輯一樣只是名稱不同
?
flow要構(gòu)造成哪個(gè)類的構(gòu)造函數(shù),該類就需要持有collect傳入的方法。這也是Collector的功能
SafeFlowCollector和Collector
Collector保存collect傳入的方法,flow擴(kuò)展Collector以使他可以觸發(fā)發(fā)射邏輯也就是flow傳入的參數(shù)。
class SafeFlowCollector<T>(val collect: Collector.() -> Unit) //擴(kuò)展函數(shù)
//發(fā)送邏輯emit,這個(gè)action是從哪里來(lái)的呢?
class Collector<T>(val action: (a: T) -> Unit) {
fun emit(value: T) {
action(value)
}
}
再解collectFunction方法:
上面說(shuō)到action方法,接著看:
fun collectFunction(a: (T) -> Unit) {
//可以看到調(diào)用collectFunction方法將傳入的方法參數(shù)保存到了Collector中也就是action方法
val co = Collector(a)
//觸發(fā)collect,collect也就是flow中傳入的方法。
co.collect()
}
原生flow,collect的demo:
?注:將上述三個(gè)類拷貝到您的項(xiàng)目中即可調(diào)用
?
Flow.flow {
Log.i(TAG, "emit before")
emit("1")
Log.i(TAG, "emit after")
}.collectFunction {
Log.i(TAG, "collectionFunction is : $it")
}
總結(jié)
因此可以看到調(diào)用collectFunction方法會(huì)調(diào)用到flow傳入的方法中,在flow傳入的方法中調(diào)用emit又會(huì)執(zhí)行collectFunction傳入的方法。
?ps:collectFunction類比于原生Flow中的collect方法即可
?
擴(kuò)展中間轉(zhuǎn)換符
flow和collect我們支持了,現(xiàn)在我們來(lái)擴(kuò)展轉(zhuǎn)換操作符。這里還是簡(jiǎn)單實(shí)現(xiàn),原生Flow的實(shí)現(xiàn)看的很繞。
map操作符
map方法接受的是一個(gè)方法。并且該方法的參數(shù)是原數(shù)據(jù),經(jīng)過(guò)轉(zhuǎn)換后返回的值是collect接受的值。
首先我們需要確定幾個(gè)點(diǎn):
1、map的參數(shù)如何確定?
?map的參數(shù)即上一個(gè)Flow emit的值,而在我們這個(gè)里面emit的值是通過(guò)flow< T >指定的,所以參數(shù)直接寫(xiě)T就可以。2.map的返回值如何確定?
?
?map的返回值要經(jīng)過(guò)兩個(gè)階段,收到上一個(gè)flow發(fā)送的值調(diào)用轉(zhuǎn)換函數(shù)把值傳入得到結(jié)果,因此map中最后一行即為返回值。
?
3.支持鏈?zhǔn)秸{(diào)用map
?map之后還需要再次經(jīng)歷map或者collectFunction方法,因此他返回的也必須是一個(gè)flowSafeCollector。參數(shù)是map傳入方法的返回值。
?
確定好這些來(lái)實(shí)現(xiàn),由于需要調(diào)用到上一個(gè)flow的collect方法, 「原生中是擴(kuò)展函數(shù)this即代表上一個(gè)Flow。本demo異曲同工,直接定義為該類的函數(shù)」 。于是實(shí)現(xiàn)如下, 「修改SafeFlowCollector」 類即可:
class SafeFlowCollector<T>(val collect: Collector.() -> Unit) {
fun collectFunction(a: (T) -> Unit) {
val co = Collector(a)
co.collect()
}
//對(duì)比發(fā)現(xiàn)只是擴(kuò)展了這個(gè)map方法
fun map(mapFunction: (T) -> R): SafeFlowCollector {
//外層調(diào)用經(jīng)map轉(zhuǎn)換后的collectionFunction會(huì)走到flow里面
return Flow.flow {
//this@SafeFlowCollector是為了保證調(diào)用到調(diào)用map函數(shù)的flow觸發(fā)這個(gè)flow的收集
this@SafeFlowCollector.collectFunction {
//當(dāng)最內(nèi)層flow函數(shù)調(diào)用emit,此collectFunction會(huì)收到回調(diào),進(jìn)行轉(zhuǎn)換后調(diào)用flow返回給最外層的collectFunction方法。
emit(mapFunction(it))
}
}
}
}
讀者可以直接將上面的SafeFlowCollector換成這個(gè)即可。
可支持map轉(zhuǎn)換Flow的demo
Flow.flow {
emit("2")
}.map {
it.toInt()
}.map {
it.toString()
}.collectFunction {
Log.i(TAG, "onCreate: it $it")
}
「最后還是放出這張圖片:」
1649984327(1).png
擴(kuò)展中間操作符
還是像map一樣,直接把該方法添加到 「SafeFlowCollector」 類中即可
//新增擴(kuò)展zip操作流,用于多個(gè)流發(fā)射,就近原則誰(shuí)先返回用誰(shuí)
//tips1:收集器類型及·返回流類型,由于返回值相同。因此復(fù)用調(diào)用方流的泛型即可
//2:開(kāi)啟收集后觸發(fā)多個(gè)流的收集,利用標(biāo)志位進(jìn)行判斷是否發(fā)射。目前采用這種方式。缺點(diǎn):流發(fā)射后應(yīng)該關(guān)閉但是此處只是限制了流的發(fā)射邏輯
fun zip(twoFlow: SafeFlowCollector<T>): SafeFlowCollector {
return Flow.flow {
var a = false //通過(guò)定義condition判斷,不支持協(xié)程需要手動(dòng)切換線程測(cè)試效果
//開(kāi)啟兩個(gè)劉的收集,誰(shuí)先收集到誰(shuí)先返回
this@SafeFlowCollector.collectFunction {
if (!a) {
a = true
this.emit(it)
}
}
twoFlow.collectFunction {
if (!a) {
a = true
this.emit(it)
}
}
}
}
?該種方案可支持多個(gè)Flow發(fā)射,zip中將參數(shù)變?yōu)榭勺儏?shù)即可。SafeCollector中擴(kuò)展
?
zip操作符Demo
val flow1 = Flow.flow<Int> {
emit(2)
}
Flow.flow<String> {
Thread{
emit("111")
}.interrupt()
}.map {
it.toInt()
}.zip(flow1).collectFunction {
Log.i(TAG, "onCreate: ZIP操作符下的Flow收集器收集到的數(shù)據(jù)位 $it")
}
?上述方案采用標(biāo)志位實(shí)現(xiàn)不太優(yōu)雅,有更好的方式可以提出~~
?
「不支持協(xié)程~~~,需要調(diào)度手動(dòng)切換線程」
總結(jié)
「轉(zhuǎn)換操作符只是中間操作符的一種,其他中間操作符原理大致都一樣。都是經(jīng)過(guò)在封裝一次flow然后觸發(fā)上級(jí)flow的收集,最后調(diào)用到最里層的flow,調(diào)用emit在一層層經(jīng)過(guò)中間操作符處理給到最外層」
-
編程
+關(guān)注
關(guān)注
88文章
3595瀏覽量
93609 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4308瀏覽量
62442 -
Flow
+關(guān)注
關(guān)注
0文章
10瀏覽量
8830
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論