【Koltin Flow(五)】SharedFlow及StateFlow

目錄

【Koltin Flow(一)】五種創(chuàng)建flow的方式
【Koltin Flow(二)】Flow操作符之末端操作符
【Koltin Flow(三)】Flow操作符之中間操作符(一)
【Koltin Flow(三)】Flow操作符之中間操作符(二)
【Koltin Flow(三)】Flow操作符之中間操作符(三)
【Koltin Flow(四)】Flow背壓
【Koltin Flow(五)】SharedFlow及StateFlow

SharedFlow

簡介

相對于Flow而言钩乍,SharedFlow為熱流怔锌,也就是說不管有無接收者埃元,都會發(fā)送值。

一阔拳、基本使用

代碼如下:
            val sharedFlow = MutableSharedFlow<Int>()
            launch {
                sharedFlow.collect {
                    Log.d(TAG.TAG,"SharedFlow $it")
                }
            }

            delay(10)
            sharedFlow.emit(1)
            sharedFlow.emit(100)
            sharedFlow.emit(100)
日志如下:
2022-08-03 10:16:53.366 4955-4981/edu.test.demo D/Test-TAG: SharedFlow 1
2022-08-03 10:16:53.366 4955-4981/edu.test.demo D/Test-TAG: SharedFlow 100
2022-08-03 10:16:53.367 4955-4981/edu.test.demo D/Test-TAG: SharedFlow 100
分析
  • 日志可以看出 基本使用的時候和flow本身沒多大區(qū)別类嗤,發(fā)送-接收遗锣。
  • 可以看出上面有個delay(10),如果沒有則可能會接收不到值泪酱,因為SharedFlow為熱流,不管有無接收者毡惜,emit都會直接發(fā)送值经伙。

二勿锅、設置訂閱重發(fā)

我們看SharedFlow構造的第一個參數(shù)replay,此參數(shù)用來設置訂閱重發(fā)的個數(shù)垮刹。
代碼如下:
            val sharedFlow = MutableSharedFlow<Int>(
                replay = 2
            )

            sharedFlow.emit(1)
            sharedFlow.emit(100)
            sharedFlow.emit(100)
            //注釋1  在此處加上重發(fā)緩存打印
            //Log.d(TAG.TAG,"SharedFlow ${sharedFlow.replayCache}")
            delay(100)

            launch {
                sharedFlow.collect {
                    Log.d(TAG.TAG,"SharedFlow $it")
                }
            }
             //注釋2 重置緩存
            //delay(100)
            //sharedFlow.resetReplayCache()
            //Log.d(TAG.TAG,"SharedFlow ${sharedFlow.replayCache}")
            //launch {
              //  sharedFlow.collect {
                //    Log.d(TAG.TAG,"SharedFlow 2 $it")
                //}
            //}
日志如下:
//注釋1 處的打印
//2022-08-03 10:23:13.621 5106-5131/edu.test.demo D/Test-TAG: SharedFlow [100, 100]
2022-08-03 10:21:14.906 5043-5070/edu.test.demo D/Test-TAG: SharedFlow 100
2022-08-03 10:21:14.906 5043-5070/edu.test.demo D/Test-TAG: SharedFlow 100
//注釋2下的打印
//2022-08-03 10:26:54.917 5233-5260/edu.test.demo D/Test-TAG: SharedFlow []
分析:
  • 可以看到驶兜,雖然是發(fā)送在前寺董,接收在后刻剥,但還是收到了兩個值造虏。因為replay重發(fā)的值設置為2,可以這樣看起來不直觀魄藕,這樣撵术,我們修改下代碼嫩与,放開注釋1出的打印,則日志會多出來【注釋1 處的打印】部分的內容饵筑,可以很清晰的看出緩存了后兩個值处坪。
  • 當然此緩存可以重置,也就是清空之前的緩存玄帕,放開注釋2處的代碼裤纹,則可以看出resetReplayCache,之后則清空了緩存锡移,后面的collect接收不到緩存的值漆际。

三奸汇、其他兩個參數(shù),可參考背壓部分的內容

四操刀、shareIn操作符

shareIn操作符是將冷流flow轉換為熱流SharedFlow婴洼,主要參數(shù)有三個
1柬采、第一個為作用域且警。
2斑芜、策略,分為三種Eagerly(立即發(fā)送)盈包、Lazily(有第一個訂閱者之后發(fā)送)醇王、
WhileSubscribed()(在第一個訂閱者出現(xiàn)之后開始寓娩、在最后一個訂閱者呼渣、消失后結束)屁置,可配置二外的參數(shù):
stopTimeoutMillis 為最后一個訂閱者小時候保留的時長畸裳,單位ms怖糊,默認為0。
replayExpirationMillis 為最后一個訂閱者消失后并徘,緩存保留的時長扰魂,單位ms劝评,默認為Long.MAX_VALUE。
3声畏、緩存重發(fā)的個數(shù)姻成。
第一種策略
代碼如下:
     val sharedFlow = (1..5).asFlow().shareIn(this, SharingStarted.Eagerly,0)
            delay(100)
            sharedFlow.collect {
                Log.d(TAG.TAG,"shareIn $it")
            }
日志沒有
分析:
  • 我們發(fā)現(xiàn)沒有日志科展,原因在哪里呢,因為轉換成熱流之后策略為Eagerly徘跪,立即開始發(fā)送垮庐,但是100ms之后才有collect乎澄,同時replay的個數(shù)為0置济,所以接收不到值锋八,如果我們將replay個數(shù)改為2护盈,則可以接收到4和5,和上面的訂閱重發(fā)是一樣的紊服。
第二種策略
代碼如下:
    val sharedFlow = (1..5).asFlow().shareIn(this, SharingStarted.Lazily, 2)
            delay(100)
            launch {
                sharedFlow.collect {
                    Log.d(TAG.TAG, "shareIn $it")
                }
            }
            delay(100)
            launch {
                sharedFlow.collect {
                    Log.d(TAG.TAG, "shareIn 2 $it")
                }
            }
日志如下:
2022-08-03 11:09:10.104 6691-6717/edu.test.demo D/Test-TAG: shareIn 1
2022-08-03 11:09:10.104 6691-6717/edu.test.demo D/Test-TAG: shareIn 2
2022-08-03 11:09:10.104 6691-6717/edu.test.demo D/Test-TAG: shareIn 3
2022-08-03 11:09:10.104 6691-6717/edu.test.demo D/Test-TAG: shareIn 4
2022-08-03 11:09:10.104 6691-6717/edu.test.demo D/Test-TAG: shareIn 5
2022-08-03 11:09:10.205 6691-6720/edu.test.demo D/Test-TAG: shareIn 2 4
2022-08-03 11:09:10.205 6691-6720/edu.test.demo D/Test-TAG: shareIn 2 5
分析:
  • 可以看出欺嗤,第一個collect能接收到全部值煎饼,那是因為Lazily是在第一個訂閱者出現(xiàn)后才發(fā)送值的校赤,但是第二個collect卻只接收到了緩存的兩個值马篮,那是因為Lazily只管第一個collect,不管后續(xù)的collect翅阵。
第三種策略
1. 采用默認參數(shù)
代碼如下:
  var time  = 0L
            time = System.currentTimeMillis()
            val sharedFlow = (1..100).asFlow().onStart {
                Log.d(TAG.TAG,"onStart ${System.currentTimeMillis() - time}")
            }.onCompletion {
                Log.d(TAG.TAG,"onCompletion ${System.currentTimeMillis() - time}")
            }.onEach {
                delay(1000)
            }.shareIn(this, SharingStarted.WhileSubscribed(), 0)

            delay(1000)
            launch {
                Log.d(TAG.TAG, "1 shareIn ${sharedFlow.first()}")
                Log.d(TAG.TAG,"1 接收到第一個值 ${System.currentTimeMillis() - time}")
            }

            delay(3000)
            launch {
                Log.d(TAG.TAG, "2 shareIn ${sharedFlow.first()}")
                Log.d(TAG.TAG,"2 接收到第一個值 ${System.currentTimeMillis() - time}")
            }
日志如下:
2022-08-03 14:00:32.842 8905-8930/edu.test.demo D/Test-TAG: onStart 1029
2022-08-03 14:00:33.844 8905-8930/edu.test.demo D/Test-TAG: 1 shareIn 1
2022-08-03 14:00:33.844 8905-8930/edu.test.demo D/Test-TAG: 1 接收到第一個值 2031
2022-08-03 14:00:33.845 8905-8931/edu.test.demo D/Test-TAG: onCompletion 2032
2022-08-03 14:00:35.839 8905-8931/edu.test.demo D/Test-TAG: onStart 4026
2022-08-03 14:00:36.841 8905-8931/edu.test.demo D/Test-TAG: 2 shareIn 1
2022-08-03 14:00:36.841 8905-8931/edu.test.demo D/Test-TAG: 2 接收到第一個值 5028
2022-08-03 14:00:36.841 8905-8930/edu.test.demo D/Test-TAG: onCompletion 5028
分析:
  • 可以看出 在first之后第一個接收者消失怎顾,所以執(zhí)行了onCompletion,也就是flow結束了幅狮,而且接收到第一個值和onCompletion的時間基本是一致的株灸。
  • 在4000ms之后第二個接收者出現(xiàn)慌烧,重新執(zhí)行了onStart ,并且在first之后也執(zhí)行了onCompletion結束了厕氨。
2.進行相關的參數(shù)配置
代碼如下:
             var time  = 0L
            time = System.currentTimeMillis()
            val sharedFlow = (1..100).asFlow().onStart {
                Log.d(TAG.TAG,"onStart ${System.currentTimeMillis() - time}")
            }.onCompletion {
                Log.d(TAG.TAG,"onCompletion ${System.currentTimeMillis() - time}")
            }.onEach {
                delay(1000)
            }.shareIn(this, SharingStarted.WhileSubscribed(
                stopTimeoutMillis = 500,
                replayExpirationMillis = 2000
            ), replay = 5)

            delay(1*1000)
            launch {
                Log.d(TAG.TAG, "1 shareIn ${sharedFlow.take(5).toList()}")
                Log.d(TAG.TAG,"1 接收到值 ${System.currentTimeMillis() - time}")
            }

            delay(10*1000)
            launch {
                Log.d(TAG.TAG, "2 shareIn ${sharedFlow.take(10).toList()}")
                Log.d(TAG.TAG,"2 接收到值 ${System.currentTimeMillis() - time}")
            }
日志如下( stopTimeoutMillis = 500,replayExpirationMillis = 2000命斧,replay = 5):
2022-08-03 14:21:15.245 9591-9616/edu.test.demo D/Test-TAG: onStart 1030
2022-08-03 14:21:20.252 9591-9616/edu.test.demo D/Test-TAG: 1 shareIn [1, 2, 3, 4, 5]
2022-08-03 14:21:20.252 9591-9616/edu.test.demo D/Test-TAG: 1 接收到值 6037
2022-08-03 14:21:20.753 9591-9616/edu.test.demo D/Test-TAG: onCompletion 6538
2022-08-03 14:21:25.243 9591-9622/edu.test.demo D/Test-TAG: onStart 11028
2022-08-03 14:21:35.253 9591-9622/edu.test.demo D/Test-TAG: 2 shareIn [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2022-08-03 14:21:35.253 9591-9622/edu.test.demo D/Test-TAG: 2 接收到值 21038
2022-08-03 14:21:35.755 9591-9617/edu.test.demo D/Test-TAG: onCompletion 21540
分析:
  • 可以看出国葬,在設置了 stopTimeoutMillis = 500之后,接收到值得時間和onCompletion的時間基本差了500ms接奈,其實就是就是延時結束鲫趁。
  • 在設置了replayExpirationMillis = 2000之后利虫,第二次開始和接收到值的時間基本就是發(fā)送10個值得時間糠惫,具體值為1-10,那是因為緩存的值已經(jīng)失效了巢价,此時這個replay = 5沒有多大意義,因為到下次的時候值已經(jīng)失效了固阁。
日志如下( stopTimeoutMillis = 500,replayExpirationMillis = Long.MAX_VALUE壤躲,replay = 5):
2022-08-03 14:47:30.144 9697-9724/edu.test.demo D/Test-TAG: onStart 1030
2022-08-03 14:47:35.154 9697-9723/edu.test.demo D/Test-TAG: 1 shareIn [1, 2, 3, 4, 5]
2022-08-03 14:47:35.154 9697-9723/edu.test.demo D/Test-TAG: 1 接收到值 6040
2022-08-03 14:47:35.655 9697-9723/edu.test.demo D/Test-TAG: onCompletion 6541
2022-08-03 14:47:40.141 9697-9726/edu.test.demo D/Test-TAG: onStart 11027
2022-08-03 14:47:45.149 9697-9727/edu.test.demo D/Test-TAG: 2 shareIn [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
2022-08-03 14:47:45.149 9697-9727/edu.test.demo D/Test-TAG: 2 接收到值 16035
2022-08-03 14:47:45.652 9697-9726/edu.test.demo D/Test-TAG: onCompletion 16538
分析:
  • 首先看區(qū)別,第一個接收者基本沒變化备燃。
  • 第二個接收者有兩點變化碉克,接收到的值變了,不再是1-10并齐,而是兩個1-5漏麦,而且第二個onStart和接收到值的時間也變了,不再是10個值的時間况褪,而是五個值的時間,原因主要如下:第一replayExpirationMillis值設置為 Long.MAX_VALUE(這也是其默認值)之后测垛,第二個接收者出現(xiàn)時捏膨,緩存并未失效,所以出現(xiàn)了前面的12345(因為replay=5食侮,緩存前五個)号涯,但是take(10)不夠了熬北,所以又出現(xiàn)了onStart,再接收五個诚隙,也就是后面的12345讶隐,時間也就是接收五個的時間。
  • 如果將第二個接收者的take(10)變成take(5)久又,緩存就直接夠了巫延,就不會出現(xiàn)第二個onStart。
  • 當然這也是因為前面是take(5)地消,如果改成take(3)炉峰,后面也就會出現(xiàn)onStart,數(shù)據(jù)變成12312脉执,那是因為雖然緩存池為5疼阔,但是只緩存了三個值,還需要兩個半夷。

StateFlow

簡介

和SharedFlow一樣婆廊,StateFlow也是熱流,但是區(qū)別在于狀態(tài)的保存巫橄,保存了最新的值淘邻,也就是新的接收者會收到最新的值,
和設置了replay = 1的SharedFlow比較類似湘换。

簡單使用

代碼如下:
            val stateFlow = MutableStateFlow(0)
            launch {
                stateFlow.collect{
                    Log.d(TAG.TAG,"stateFlow 1 collect $it")
                }
            }
            delay(1000)
            stateFlow.value = 10
            delay(1000)
            stateFlow.value = 10
            delay(1000)
            stateFlow.value = 11
            launch {
                stateFlow.collect{
                    Log.d(TAG.TAG,"stateFlow 2 collect $it")
                }
            }

日志如下:
2022-08-03 15:43:14.618 11669-11705/edu.test.demo D/Test-TAG: stateFlow 1 collect 0
2022-08-03 15:43:15.623 11669-11704/edu.test.demo D/Test-TAG: stateFlow 1 collect 10
2022-08-03 15:43:17.626 11669-11705/edu.test.demo D/Test-TAG: stateFlow 1 collect 11
2022-08-03 15:43:17.626 11669-11705/edu.test.demo D/Test-TAG: stateFlow 2 collect 11
分析:
  • 可以看到接收者1剛開始收到了初始值1宾舅,那是因為StateFlow在每個接收者出現(xiàn)時都會接收到最新的值。
  • 接收者1后面又接收到了10彩倚,那是動態(tài)更新StateFlow的value值觸發(fā)的筹我,但是注意10發(fā)送了兩次,但是只接收到了一次帆离,說明StateFlow是天然防抖的蔬蕊,連續(xù)發(fā)送兩次同樣的值,只會接收一次盯质,后面又接收到了11和第一個10效果一致袁串,是value值觸發(fā)的概而。
  • 接收者2接收到11的道理和接收者1接收到1的道理是一樣的呼巷。

stateIn操作符

stateIn操作符是將冷流flow轉換為熱流StateFlow,主要參數(shù)有三個
1赎瑰、第一個為作用域王悍。
2、策略餐曼,分為三種Eagerly(立即發(fā)送)压储、Lazily(有第一個訂閱者之后發(fā)送)鲜漩、
WhileSubscribed()(在第一個訂閱者出現(xiàn)之后開始、在最后一個訂閱者集惋、消失后結束)孕似,可配置二外的參數(shù):
stopTimeoutMillis 為最后一個訂閱者小時候保留的時長,單位ms刮刑,默認為0喉祭。
replayExpirationMillis 為最后一個訂閱者消失后,緩存保留的時長雷绢,單位ms泛烙,默認為Long.MAX_VALUE。
3翘紊、StateFlow的初始值蔽氨。
策略基本和shareIn是一致的,只是replay固定為1帆疟,另外會有一個初始值鹉究。
分析一種,其他的和shareIn類比即可踪宠,策略為WhileSubscribed()坊饶。
代碼如下:
             var time = 0L
            time = System.currentTimeMillis()
            val stateFlow = (1..10).asFlow().onStart {
                Log.d(TAG.TAG,"onStart ${System.currentTimeMillis() - time}")
            }.onCompletion {
                Log.d(TAG.TAG,"onCompletion ${System.currentTimeMillis() - time}")
            }.onEach {
                delay(1000)
            }.stateIn(this, SharingStarted.WhileSubscribed(),0)

            launch {
                Log.d(TAG.TAG, "stateFlow 1 collect ${stateFlow.take(5).toList()}")
                Log.d(TAG.TAG,"接收到值 ${System.currentTimeMillis() - time}")
            }
            delay(10*1000)
            launch {
                Log.d(TAG.TAG, "stateFlow 2 collect ${stateFlow.take(5).toList()}")
                Log.d(TAG.TAG,"接收到值 ${System.currentTimeMillis() - time}")
            }
日志如下:
2022-08-03 15:33:35.204 11353-11379/edu.test.demo D/Test-TAG: onStart 70
2022-08-03 15:33:39.219 11353-11380/edu.test.demo D/Test-TAG: stateFlow 1 collect [0, 1, 2, 3, 4]
2022-08-03 15:33:39.219 11353-11380/edu.test.demo D/Test-TAG: 接收到值 4101
2022-08-03 15:33:39.221 11353-11378/edu.test.demo D/Test-TAG: onCompletion 4102
2022-08-03 15:33:45.145 11353-11378/edu.test.demo D/Test-TAG: onStart 10027
2022-08-03 15:33:49.151 11353-11378/edu.test.demo D/Test-TAG: stateFlow 2 collect [4, 1, 2, 3, 4]
2022-08-03 15:33:49.151 11353-11378/edu.test.demo D/Test-TAG: 接收到值 14033
2022-08-03 15:33:49.151 11353-11380/edu.test.demo D/Test-TAG: onCompletion 14033
分析:
  • 可以看到接收者1接收到的值為01234,因為有一個初始值為0殴蓬,占了一個位置匿级,開始到接收到值的時間也基本是個值的時間,接收到之后也打印了onCompletion染厅,和接收到值的時間基本一致痘绎。
  • 第二個接收者,接收到的值為41234肖粮,因為StateFlow緩存了一個最新值4孤页,再接收四個新值,時間和接收者1類似涩馆。

總結

  • 本篇主要介紹了SharedFlow和StateFlow的基本使用行施、以及參數(shù)設置相關內容。
  • 本篇也終點介紹了shareIn操作符的使用魂那,以及各種策略參數(shù)的設置蛾号,stateIn類比shareIn理解。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末涯雅,一起剝皮案震驚了整個濱河市鲜结,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖精刷,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拗胜,死亡現(xiàn)場離奇詭異,居然都是意外死亡怒允,警方通過查閱死者的電腦和手機埂软,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纫事,“玉大人仰美,你說我怎么就攤上這事《瘢” “怎么了咖杂?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蚊夫。 經(jīng)常有香客問我诉字,道長,這世上最難降的妖魔是什么知纷? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任壤圃,我火速辦了婚禮,結果婚禮上琅轧,老公的妹妹穿的比我還像新娘伍绳。我一直安慰自己,他們只是感情好乍桂,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布冲杀。 她就那樣靜靜地躺著,像睡著了一般睹酌。 火紅的嫁衣襯著肌膚如雪权谁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天憋沿,我揣著相機與錄音旺芽,去河邊找鬼。 笑死辐啄,一個胖子當著我的面吹牛采章,可吹牛的內容都是我干的。 我是一名探鬼主播壶辜,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼悯舟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了士复?” 一聲冷哼從身側響起图谷,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤翩活,失蹤者是張志新(化名)和其女友劉穎阱洪,沒想到半個月后便贵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡冗荸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年承璃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚌本。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡盔粹,死狀恐怖,靈堂內的尸體忽然破棺而出程癌,到底是詐尸還是另有隱情舷嗡,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布嵌莉,位于F島的核電站进萄,受9級特大地震影響,放射性物質發(fā)生泄漏锐峭。R本人自食惡果不足惜中鼠,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沿癞。 院中可真熱鬧援雇,春花似錦、人聲如沸椎扬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚕涤。三九已至晶府,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钻趋,已是汗流浹背川陆。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛮位,地道東北人较沪。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像失仁,于是被迫代替她去往敵國和親尸曼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內容