淺析Kotlin_Flow

一. 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ù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內(nèi)容