目錄
【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理解。