一. Kotlin Flow 介紹
Flow — cold asynchronous stream with flow builder and comprehensive operator set (filter, map, etc);
流 - 冷的異步流,具有流生成器和全面的操作符集(過濾器、map等)。
Flow 就是 Kotlin 協(xié)程與響應(yīng)式編程模型結(jié)合的產(chǎn)物,你會發(fā)現(xiàn)它與 RxJava 非常像。
Flow有以下特點(diǎn):
1.冷數(shù)據(jù)流,不消費(fèi)則不生產(chǎn),這一點(diǎn)與Channel正相反:Channel的發(fā)送端并不依賴于接收端。
2.Flow通過flowOn改變數(shù)據(jù)發(fā)射的線程,數(shù)據(jù)消費(fèi)線程則由協(xié)程所在線程決定
3.與RxJava類似,支持通過catch捕獲異常,通過onCompletion回調(diào)完成
4.Flow沒有提供取消方法,可以通過取消Flow所在協(xié)程的方式來取消
二. Flow 基本使用
runBlocking {
flow {
for (i in 1..6) {
//發(fā)射數(shù)據(jù)
emit(i)
println("emit:$i")
}
}.catch {
//異常處理
println("catch")
}.onCompletion {
//完成回調(diào)
println("onCompletion")
}.collect { num ->
// 具體的消費(fèi)處理
// 只有collect時(shí)才會生產(chǎn)數(shù)據(jù),原因之后會講
println("collect:$num")
}
}
輸出:
collect:1
emit:1
collect:2
emit:2
collect:3
emit:3
collect:4
emit:4
collect:5
emit:5
collect:6
emit:6
onCompletion
2.1 創(chuàng)建 flow
除了剛剛展示的 flow builder 可以用于創(chuàng)建 flow,還有其他的幾種方式:
1.使用flowOf可以定義一組固定的值
fun simple() = flowOf(1,2,3)
2.可以使用 asFlow() 擴(kuò)展函數(shù)將各種集合和序列轉(zhuǎn)換為流。
suspend fun simple() = listOf(1,2,3).asFlow().collect { println(it) }
2.2 切換線程
相比于 RxJava 需要使用 observeOn、subscribeOn 來切換線程,flow 會更加簡單。只需使用 flowOn,廢話不多說,舉個(gè)??。
runBlocking {
flow {
for (i in 0..3) {
println("Emit Flow in ${Thread.currentThread().name}")
emit(i)
}
}.flowOn(Dispatchers.IO).map {
println("Map Flow in ${Thread.currentThread().name}")
it * it
}.collect {
println("Collect Flow in ${Thread.currentThread().name}")
println("Result---$it")
}
}
執(zhí)行結(jié)果:
Emit Flow in DefaultDispatcher-worker-1
Emit Flow in DefaultDispatcher-worker-1
Emit Flow in DefaultDispatcher-worker-1
Emit Flow in DefaultDispatcher-worker-1
Map Flow in main
Collect Flow in main
Result---0
Map Flow in main
Collect Flow in main
Result---1
Map Flow in main
Collect Flow in main
Result---4
Map Flow in main
Collect Flow in main
Result---9
由這個(gè)例子可以看出,開發(fā)人員可以通過flowOn()改變的是Flow函數(shù)內(nèi)部發(fā)射數(shù)據(jù)時(shí)的線程,而在collect收集數(shù)據(jù)時(shí)會自動切回創(chuàng)建Flow時(shí)的線程。
Flow的調(diào)度器 API 中看似只有flowOn與subscribeOn對應(yīng),其實(shí)collect所在協(xié)程的調(diào)度器也與observeOn指定的調(diào)度器對應(yīng)。
對比類型 | Flow | RxJava |
---|---|---|
改變數(shù)據(jù)發(fā)送的線程 | flowOn | subscribeOn |
改變消費(fèi)數(shù)據(jù)的線程 | 它自動切回所在協(xié)程的調(diào)度器 | observeOn |
flowOn只對它上面的代碼有效。下面看下一個(gè)
runBlocking {
flow {
for (i in 0..3) {
println("Emit Flow in ${Thread.currentThread().name}")
emit(i)
}
}.map {
println("Map Flow in ${Thread.currentThread().name}")
it * it
}.flowOn(Dispatchers.IO).collect {
println("Collect Flow in ${Thread.currentThread().name}")
println("Result---$it")
}
}
執(zhí)行結(jié)果:
Emit Flow in DefaultDispatcher-worker-1
Map Flow in DefaultDispatcher-worker-1
Emit Flow in DefaultDispatcher-worker-1
Map Flow in DefaultDispatcher-worker-1
Emit Flow in DefaultDispatcher-worker-1
Map Flow in DefaultDispatcher-worker-1
Emit Flow in DefaultDispatcher-worker-1
Map Flow in DefaultDispatcher-worker-1
Collect Flow in main
Result---0
Collect Flow in main
Result---1
Collect Flow in main
Result---4
Collect Flow in main
Result---9
這兩個(gè)例子只是改變了flowOn的位置,map的工作線程就變了。map操作符移動到flowOn上面,map操作就會再flowOn指定的線程操作。
flowOn操作符影響的是上游的操作,而 collect() 指定哪個(gè)線程,則需要看整個(gè) flow 處于哪個(gè) CoroutineScope 下。
注意:不允許在內(nèi)部使用withContext()來切換flow的線程。因?yàn)閒low不是線程安全的,如果一定要這么做,請使用channelFlow。
2.3 flow異常處理
low的異常處理也比較直接,直接調(diào)用 catch 函數(shù)即可:
runBlocking {
flow {
emit(1)
throw NullPointerException()
}.catch {
println("catch->$it")
}.collect {
println("collect->$it")
}
}
執(zhí)行結(jié)果:
collect->1
catch->java.lang.NullPointerException
在flow的參數(shù)中拋了一個(gè)空指針異常,在catch函數(shù)中就可以直接捕獲到這個(gè)異常。如果沒有調(diào)用catch函數(shù),未捕獲的異常會在消費(fèi)時(shí)拋出。和collect一樣,catch 函數(shù)只能捕獲它的上游的異常。
Flow中的catch對應(yīng)著 RxJava 中的 onError。
當(dāng)然,還可以使用try{}catch{}塊來捕獲異常。??如下
runBlocking {
flow {
try {
emit(1)
throw NullPointerException()
} catch (it: Exception) {
println("catch->$it")
}
}.collect {
println("collect->$it")
}
}
輸出結(jié)果一樣,這里就不再展示了。
2.6 flow 完成
如果我們想要在流完成時(shí)執(zhí)行邏輯,可以使用 onCompletion:
runBlocking {
flow {
emit(1)
}.onCompletion {
println("onCompletion")
}.collect {
println("collect->$it")
}
}
執(zhí)行結(jié)果:
collect->1
onCompletion
也可以使用try{}finally{}塊在收集完成時(shí)執(zhí)行一個(gè)動作。代碼和try{}catch{}差不多。
2.5 flow 取消
flow需要在協(xié)程里面使用,因?yàn)閏ollect是掛起函數(shù),flow基于冷流的特性,不調(diào)用collect構(gòu)建器的代碼壓根不會走。
所以 flow 在一個(gè)掛起函數(shù)內(nèi)被掛起了, flow 才能被取消。
runBlocking {
val f = flow {
for (i in 1..3) {
delay(500)
println("emit $i")
emit(i)
}
}
withTimeoutOrNull(1600) {
f.collect {
delay(500)
println("consume $it")
}
}
println("cancel")
}
執(zhí)行結(jié)果:
emit 1
consume 1
emit 2
cancel
三、Flow 操作符
3.1 過渡流操作符
過渡操作符可以對流進(jìn)行轉(zhuǎn)換,從上游獲取數(shù)據(jù)做一定更改,然后返給下游。
map
我們可以在 map 中執(zhí)行一些過渡操作,比如本例中將上游發(fā)送的數(shù)據(jù)*9,然后再發(fā)射給下游
runBlocking {
(1..6).asFlow().map {
it * 9
}.collect {
println(it)
}
}
執(zhí)行結(jié)果:
9
18
27
36
45
54
通常我們的代碼中有多個(gè)接口需要連續(xù)調(diào)用的時(shí)候就很適合用這種方法,可以十分有效的避免接口調(diào)用嵌套。
zip
合并兩個(gè)flow數(shù)據(jù)流,會分別對兩個(gè)流合并處理,也就是快的流要等慢的流發(fā)射完才能合并。一般用作合并兩個(gè)網(wǎng)絡(luò)請求返回?cái)?shù)據(jù)。
runBlocking {
val nums = (1..3).asFlow().onEach { delay(100) }
val strs = flowOf("one", "two", "three","four").onEach { delay(200) }
val startTime = System.currentTimeMillis() // 記錄開始的時(shí)間
nums.zip(strs) { a, b -> "$a -> $b" } // 使用“zip”組合單個(gè)字符串
.collect { value ->
println("$value at ${System.currentTimeMillis() - startTime} ms from start")
}
}
執(zhí)行結(jié)果:
1 -> one at 274 ms from start
2 -> two at 491 ms from start
3 -> three at 692 ms from start
combine
使用 combine 合并時(shí),每次從 flowA 發(fā)出新的 item ,會將其與 flowB 的最新的 item 合并。
runBlocking {
val nums = (1..3).asFlow().onEach { delay(100) }
val strs = flowOf("one", "two", "three","four").onEach { delay(200) }
val startTime = System.currentTimeMillis() // 記錄開始的時(shí)間
nums.combine(strs) { a, b -> "$a -> $b" }
.collect { value ->
println("$value at ${System.currentTimeMillis() - startTime} ms from start")
}
}
執(zhí)行結(jié)果:
1 -> one at 297 ms from start
2 -> one at 312 ms from start
3 -> one at 422 ms from start
3 -> two at 500 ms from start
3 -> three at 703 ms from start
3 -> four at 906 ms from start
filter
filter是過濾操作,看代碼
runBlocking {
(1..5).asFlow()
.filter {
println("Filter $it")
it % 2 == 0
}.map {
println("Map $it")
"string $it"
}.collect {
println("Collect $it")
}
}
執(zhí)行結(jié)果:
Filter 1
Filter 2
Map 2
Collect string 2
Filter 3
Filter 4
Map 4
Collect string 4
Filter 5
3.2 轉(zhuǎn)換操作符
transform
它可以用來模仿簡單的轉(zhuǎn)換,比如 map 和 filter,也可以實(shí)現(xiàn)更復(fù)雜的轉(zhuǎn)換。 使用transform,我們可以發(fā)出任意次數(shù)的任意值。??來啦
runBlocking {
(1..3).asFlow()
.transform {
emit("Making request $it")
delay(1000)
emit("response $it")
}
.collect {
println(it)
}
}
執(zhí)行結(jié)果:
Making request 1
response 1
Making request 2
response 2
Making request 3
response 3
3.3 限長操作符
take
在流達(dá)到take設(shè)置的限制時(shí)會將它的執(zhí)行取消。協(xié)程中的取消總是通過拋出異常來執(zhí)行,所以需要加上try...catch
runBlocking {
flow {
try {
emit(1)
emit(2)
emit(3)
} finally {
println("Finally in numbers")
}
}.take(2)
.collect { println(it) }
}
執(zhí)行結(jié)果:
1
2
Finally in numbers
3.4 末端流操作符
末端流操作符就是下游調(diào)用的操作符。比如collect
toList
會把數(shù)據(jù)消費(fèi)到轉(zhuǎn)成List
runBlocking {
println((1..9).asFlow().filter { it % 2 == 0 }.toList())
}
執(zhí)行結(jié)果:
[2, 4, 6, 8]
toSet和toList類似
frist
獲取第一個(gè)元素。如果流為空,則拋出 NoSuchElementException。
runBlocking {
println((1..9).asFlow().filter { it % 2 == 0 }.first())
}
執(zhí)行結(jié)果:
2
reduce
reduce的第一個(gè)參數(shù)是上次操作的結(jié)果,第二個(gè)參數(shù)是當(dāng)前需要傳入的值。它的返回值類型必須和集合的元素類型相符
runBlocking {
val sum = (1..5).asFlow()
.map { it * it }
.reduce { a, b ->
println("a->$a b->$b")
a + b
}
println("sum->$sum")
}
執(zhí)行結(jié)果:
a->1 b->4
a->5 b->9
a->14 b->16
a->30 b->25
sum->55
fold
fold的返回值類型則不受約束。
runBlocking {
val numbers = listOf(1, 1, 1)
val result = numbers.fold(StringBuilder()) { str: StringBuilder, i: Int ->
str.append(i).append(" ")
}
println("foldResult=$result")
}
執(zhí)行結(jié)果:
foldResult=1 1 1
onEach
上游的每個(gè)值向下游發(fā)出之前調(diào)用onEach操作的流。
runBlocking {
(1..3).asFlow().onEach {
println("onEach->$it")
}.collect {
println("collect->$it")
}
}
執(zhí)行結(jié)果:
onEach->1
collect->1
onEach->2
collect->2
onEach->3
collect->3
3.5 展平流
展平流就是處理Flow<Flow<T>>這種流包含流的這種情況,讓它通過不同的方式展開鋪平。
flatMapConcat
串行處理數(shù)據(jù),展開合并成一個(gè)流
fun requestFlow(i: Int): Flow<String> = flow {
emit("$i: First")
delay(500) // 等待 500 毫秒
emit("$i: Second")
}
fun main() = runBlocking<Unit> {
val startTime = System.currentTimeMillis() // 記錄開始時(shí)間
(1..3).asFlow().onEach { delay(100) } // 每 100 毫秒發(fā)射一個(gè)數(shù)字
.flatMapConcat { requestFlow(it) }
.collect { value -> // 收集并打印
println("$value at ${System.currentTimeMillis() - startTime} ms from start")
}
}
執(zhí)行結(jié)果:
1: First at 172 ms from start
1: Second at 688 ms from start
2: First at 797 ms from start
2: Second at 1312 ms from start
3: First at 1422 ms from start
3: Second at 1937 ms from start
這個(gè)相當(dāng)于把兩個(gè)流鏈接起來了,相當(dāng)于串聯(lián)。
flatMapMerge
并發(fā)收集所有傳入的流,并將它們的值合并到一個(gè)單獨(dú)的流,以便盡快的發(fā)射值。
fun requestFlow(i: Int): Flow<String> = flow {
emit("$i: First")
delay(500) // 等待 500 毫秒
emit("$i: Second")
}
runBlocking {
val startTime = System.currentTimeMillis() // 記錄開始時(shí)間
(1..6).asFlow().onEach { delay(100) } // 每 100 毫秒發(fā)射一個(gè)數(shù)字
.flatMapMerge { requestFlow(it) }
.collect { value -> // 收集并打印
println("$value at ${System.currentTimeMillis() - startTime} ms from start")
}
}
執(zhí)行結(jié)果:
1: First at 265 ms from start
2: First at 359 ms from start
3: First at 469 ms from start
4: First at 578 ms from start
5: First at 687 ms from start
1: Second at 781 ms from start
6: First at 796 ms from start
2: Second at 875 ms from start
3: Second at 984 ms from start
4: Second at 1093 ms from start
5: Second at 1203 ms from start
6: Second at 1312 ms from start
flatMapLatest
與 collectLatest操作符類似,也有相對應(yīng)的“最新”展平模式,在發(fā)出新流后立即取消先前流的收集。
runBlocking {
val startTime = System.currentTimeMillis() // 記錄開始時(shí)間
(1..3).asFlow().onEach { delay(100) } // 每 100 毫秒發(fā)射一個(gè)數(shù)字
.flatMapLatest { requestFlow(it) }
.collect { value -> // 收集并打印
println("$value at ${System.currentTimeMillis() - startTime} ms from start")
}
}
執(zhí)行結(jié)果:
1: First at 220 ms from start
2: First at 345 ms from start
3: First at 454 ms from start
3: Second at 957 ms from start
四、Flow 背壓處理
什么是背壓?就是在生產(chǎn)者的生產(chǎn)速率高于消費(fèi)者的處理速率的情況下出現(xiàn),發(fā)射的量大于消費(fèi)的量,造成了阻塞,就相當(dāng)于壓力往回走,這就是背壓。
Kotlin協(xié)程支持背壓。Kotlin流程設(shè)計(jì)中的所有函數(shù)都標(biāo)有suspend修飾符-具有在不阻塞線程的情況下掛起調(diào)用程序執(zhí)行的強(qiáng)大功能。因此,當(dāng)流的收集器不堪重負(fù)時(shí),它可以簡單地掛起發(fā)射器,并在準(zhǔn)備好接受更多元素時(shí)稍后將其恢復(fù)。
4.1 buffer操作符
buffer參數(shù) | 處理策略 |
---|---|
BufferOverflow.SUSPEND | 設(shè)置緩沖區(qū),如果溢出了,則將當(dāng)前協(xié)程掛起,直到有消費(fèi)了緩沖區(qū)中的數(shù)據(jù) |
BufferOverflow.DROP_LATEST | 設(shè)置緩沖區(qū),如果溢出了,丟棄最新的數(shù)據(jù) |
BufferOverflow.DROP_OLDEST | 設(shè)置緩沖區(qū),如果溢出了,丟棄最老的數(shù)據(jù) |
4.1.1 采用BufferOverflow.SUSPEND
suspend fun flowBackpressureBuffer(overflow: BufferOverflow) {
fun currTime() = System.currentTimeMillis()
var start: Long = 0
val time = measureTimeMillis {
(1..5).asFlow()
.onStart { start = currTime() }
.onEach {
delay(100)
println("emit $it (${currTime() - start}ms)")
}
.buffer(capacity = 2, overflow)
.collect {
println("collect $it starts (${currTime() - start}ms) ")
delay(500)
println("collect $it ends (${currTime() - start}ms) ")
}
}
println("Cost $time ms")
}
執(zhí)行結(jié)果:
emit 1 (115ms)
collect 1 starts (121ms)
emit 2 (234ms)
emit 3 (343ms)
emit 4 (452ms)
collect 1 ends (621ms)
collect 2 starts (621ms)
emit 5 (732ms)
collect 2 ends (1136ms)
collect 3 starts (1136ms)
collect 3 ends (1653ms)
collect 4 starts (1653ms)
collect 4 ends (2156ms)
collect 5 starts (2156ms)
collect 5 ends (2668ms)
Cost 2775 ms
可以看到“emit 4 (452ms)”發(fā)射后,發(fā)現(xiàn)緩沖區(qū)滿了,所以就掛起了,當(dāng)開始“collect 2 starts (621ms) ”的時(shí)候,此時(shí) 3 和 4 存儲在了緩沖區(qū)內(nèi),此時(shí)發(fā)射第5個(gè)。因?yàn)?buffer 的容量是從 0 開始計(jì)算的。
4.1.2 采用BufferOverflow.DROP_LATEST
還是上面的代碼,只是buffer操作符的第二個(gè)參數(shù)變成了BufferOverflow.DROP_LATEST 。
執(zhí)行結(jié)果如下:
emit 1 (121ms)
collect 1 starts (126ms)
emit 2 (227ms)
emit 3 (337ms)
emit 4 (448ms)
emit 5 (557ms)
collect 1 ends (636ms)
collect 2 starts (636ms)
collect 2 ends (1137ms)
collect 3 starts (1137ms)
collect 3 ends (1651ms)
Cost 1761 ms
從日志可以看到雖然發(fā)射了五個(gè),但是丟棄了后面的4和5。
4.1.3 采用BufferOverflow.DROP_OLDEST
還是上面的代碼,只是buffer操作符的第二個(gè)參數(shù)變成了BufferOverflow.DROP_OLDEST 。
執(zhí)行結(jié)果如下:
emit 1 (117ms)
collect 1 starts (121ms)
emit 2 (223ms)
emit 3 (332ms)
emit 4 (440ms)
emit 5 (550ms)
collect 1 ends (628ms)
collect 4 starts (628ms)
collect 4 ends (1139ms)
collect 5 starts (1139ms)
collect 5 ends (1653ms)
Cost 1749 ms
從日志可以看到雖然發(fā)射了五個(gè),但是丟棄了前面的 2和3。
4.2 conflate操作符
confilate 操作符是不設(shè)緩沖區(qū),丟棄舊數(shù)據(jù)。
suspend fun flowBackpressureBuffer(overflow: BufferOverflow) {
fun currTime() = System.currentTimeMillis()
var start: Long = 0
val time = measureTimeMillis {
(1..5).asFlow()
.onStart { start = currTime() }
.onEach {
delay(100)
println("emit $it (${currTime() - start}ms)")
}
.conflate()
.collect {
println("collect $it starts (${currTime() - start}ms) ")
delay(500)
println("collect $it ends (${currTime() - start}ms) ")
}
}
println("Cost $time ms")
}
執(zhí)行結(jié)果如下:
emit 1 (116ms)
collect 1 starts (121ms)
emit 2 (222ms)
emit 3 (329ms)
emit 4 (437ms)
emit 5 (546ms)
collect 1 ends (624ms)
collect 5 starts (624ms)
collect 5 ends (1138ms)
Cost 1252 ms
conflate 操作符是不設(shè)緩沖區(qū),也就是緩沖區(qū)大小為 0,丟棄舊數(shù)據(jù),也就是采取 DROP_OLDEST 策略,那么相當(dāng)于 buffer(0, BufferOverflow.DROP_OLDEST) 。
看一下源碼:
public fun <T> Flow<T>.conflate(): Flow<T> = buffer(CONFLATED)
public fun <T> Flow<T>.buffer(capacity: Int = BUFFERED, onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND): Flow<T> {
require(capacity >= 0 || capacity == BUFFERED || capacity == CONFLATED) {
"Buffer size should be non-negative, BUFFERED, or CONFLATED, but was $capacity"
}
require(capacity != CONFLATED || onBufferOverflow == BufferOverflow.SUSPEND) {
"CONFLATED capacity cannot be used with non-default onBufferOverflow"
}
// desugar CONFLATED capacity to (0, DROP_OLDEST)
var capacity = capacity
var onBufferOverflow = onBufferOverflow
**if (capacity == CONFLATED) {
capacity = 0
onBufferOverflow = BufferOverflow.DROP_OLDEST
}**
// create a flow
return when (this) {
is FusibleFlow -> fuse(capacity = capacity, onBufferOverflow = onBufferOverflow)
else -> ChannelFlowOperatorImpl(this, capacity = capacity, onBufferOverflow = onBufferOverflow)
}
}
從源碼可以看出,conflate 是 buffer(0, BufferOverflow.DROP_OLDEST) 的一種快捷方式。
4.3 collectLatest操作符
這個(gè)操作符只接受最后一個(gè)發(fā)射。
runBlocking {
flow<Int> {
(1..3).forEach {
delay(100)
emit(it)
println("emit->$it")
}
}.collectLatest {
delay(800)
println("collectLatest->$it")
}
}
執(zhí)行結(jié)果如下:
emit->1
emit->2
emit->3
collectLatest->3
五、StateFlow 和 SharedFlow
5.1 StateFlow
StateFlow 和 LiveData 差不多,都是可觀察的數(shù)據(jù)容器。在 StateFlow 中任何數(shù)據(jù)的發(fā)送,它的每一個(gè)接收器都能接收到。
和 LiveData 不同的是, LiveData 不需要初始值,但 StateFlow 需要。
LiveData 會與 Activity 綁定,當(dāng) View 進(jìn)入 STOPED 狀態(tài)時(shí), LiveData.observer() 會自動取消注冊,而從 StateFlow 或任意其他數(shù)據(jù)流收集數(shù)據(jù)的操作并不會停止。如需實(shí)現(xiàn)相同的行為,需要從 Lifecycle.repeatOnLifecycle 塊收集數(shù)據(jù)流。
StateFlow 是熱流,并不是冷流。并且 StateFlow 的 collect 收不到調(diào)用之前發(fā)射的數(shù)據(jù)。
val state = MutableStateFlow(1)
runBlocking {
coroutineScope {
launch {
for (i in 0..10) {
state.emit(i)
delay(1000)
}
}
launch {
delay(2100)
state.collect {
println("receive state $it")
}
}
}
}
執(zhí)行結(jié)果如下:
receive state 2
receive state 3
receive state 4
receive state 5
receive state 6
receive state 7
receive state 8
receive state 9
receive state 10
因?yàn)槭茄訒r(shí)2.1s,所以最后是從 2 開始接收的。
StateFlow 分為 StateFlow 和 MutableStateFlow 。就像 LiveData 和 MutableLiveData 一樣。 StateFlow 只能接收數(shù)據(jù),不能發(fā)送數(shù)據(jù),而 MutableStateFlow 即可以發(fā)送也可以接收。
5.2 SharedFlow
SharedFlow 和 StateFlow 相比,他有緩沖區(qū)區(qū),并可以定義緩沖區(qū)的溢出規(guī)則,已經(jīng)可以定義給一個(gè)新的接收器發(fā)送多少數(shù)據(jù)的緩存值。MutableSharedFlow 不需要初始值。
當(dāng)你有如下場景時(shí),需要使用 SharedFlow:
- 發(fā)生訂閱時(shí),需要將過去已經(jīng)更新的n個(gè)值,同步給新的訂閱者。
- 配置緩存策略。
MutableSharedFlow 的參數(shù)如下:
- replay 當(dāng)新的訂閱者Collect時(shí),發(fā)送幾個(gè)已經(jīng)發(fā)送過的數(shù)據(jù)給它
- extraBufferCapacity 減去replay,MutableSharedFlow還緩存多少數(shù)據(jù)
- onBufferOverflow 緩存策略
- SUSPEND 掛起
- DROP_OLDEST 丟掉最舊值
- DROP_LATEST 丟掉最新值
上代碼,
suspend fun simpleSharedFlow() {
val sharedFlow = MutableSharedFlow<Int>(
replay = 5,
extraBufferCapacity = 3,
)
coroutineScope {
launch {
sharedFlow.collect {
println("collect1 received shared flow $it")
}
}
launch {
(1..10).forEach {
sharedFlow.emit(it)
delay(100)
}
}
// wait a minute
delay(1000)
launch {
sharedFlow.collect {
println("collect2 received shared flow $it")
}
}
}
}
執(zhí)行結(jié)果如下:
collect1 received shared flow 1
collect1 received shared flow 2
collect1 received shared flow 3
collect1 received shared flow 4
collect1 received shared flow 5
collect1 received shared flow 6
collect1 received shared flow 7
collect1 received shared flow 8
collect1 received shared flow 9
collect1 received shared flow 10
collect2 received shared flow 6
collect2 received shared flow 7
collect2 received shared flow 8
collect2 received shared flow 9
collect2 received shared flow 10
因?yàn)閞eplay = 5,所以collect2收到了5個(gè)已經(jīng)發(fā)送的數(shù)據(jù)。