flow 终极系列

https://www.youtube.com/watch?v=sk3svS_fzZM&list=PLQkwcJG4YTCQHCppNAQmLsj_jW38rU9sC&index=52

1. flow 的创建

在viewmodel中创建一个计时倒数的flow

    val countDownFlow = flow<Int> {
        val startValue = 10
        var curValue = startValue
        while (curValue > 0) {
            delay(1000L)
            curValue--
            emit(curValue)
        }
    }

在compose中使用这个flow 

val time by flowDemoViewModel.countDownFlow.collectAsState(10)
Text(text = time.toString())

可以看到,页面启动后,text的数字有10变到0

抛开界面,单纯的收集流

   fun collectFlow() {
        viewModelScope.launch {
            countDownFlow.collect {time->
                println("The current time is $time")
            }

        }
    }

启动的时候开始收集

  LaunchedEffect(null) {
                        flowDemoViewModel.collectFlow()
                    }

流输出

2. collect和 collectLatest 有什么区别呢?

我们在发送和接收的地方都添加上打印

 //创建示例
    val countDownFlow = flow<Int> {
        val startValue = 10
        var curValue = startValue
        while (curValue > 0) {
            delay(1000L)
            curValue--
            println("The current emit $curValue")
            emit(curValue)
        }
    }

    fun collectFlow() {
        println("The current time collect")
        viewModelScope.launch {
            countDownFlow.collectLatest {time->
                println("The current time is start $time")
                delay(1500)
                println("The current time is end $time")
            }

        }
    }

先用collect,看下打印效果

countDownFlow.collect {}

数据发送端是一秒发送一个,但是接收地方需要1.5秒处理,但是使用collect的话,整个数据还是按顺序下来的

改成countDownFlow.collectLatest {}

collectLatest不会等待下游把事件处理完,而是继续发送,当下游收到新的事件后,会中止前面一个事件的处理,所以,我们只有看到,最后一个事件被处理完成,其他的都是知识开始,就中止了

对这个countdownflow的数据收集,数据流是独立,互不受影响的,例如我们对同一个流进行收集,一个用

val time = flowDemoViewModel.countDownFlow.collectAsStateWithLifecycle(10)
的方式,

一个用 countDownFlow.collectLatest {}

数据发送端其实是两个互不影响的流在发送,可以看到emit发送了两次

3.flow简单操作符filter,map,onEach

fun operatorFlow(){
        viewModelScope.launch {
            countDownFlow.filter {
                it%2==0
            }.onEach {
                println("The current onEach $it")
            }.map {
                it*it
            }.collect{
                println("The current is $it")
            }
        }
    }

filter 过滤,只放行符合条件的数据

onEach对每个数据进行一些读的操作,继续发送值

map 对数据进行一些转换后,发送转换后的数据

4.flow terminal操作符 count,reduce,fold

count对流中的数据个数进行统计

    fun countFlow() {
        viewModelScope.launch {
            val count = countDownFlow.filter {
                it % 2 == 0
            }.onEach {
                println("The current terminalFlow onEach $it")
            }.count()
            println("The current count terminalFlow is $count")

        }
    }

reduce

reduce 发送了5,4,3,2,1,过滤后只有5,3,1 然后累计成

5*3=15

15*1=15

最后reduce=15

 fun reduceFlow() {
        viewModelScope.launch {
            val reduce = countDownFlow.filter {
                it % 2 == 1
            }.onEach {
                println("The current terminalFlow onEach $it")
            }.reduce { accumulator, value ->
                //accumulator前面累计的值
                //value新的数据
                // accumulator+value //累计加
                accumulator * value //累计乘
            }
            println("The current reduce terminalFlow is $reduce")

        }

fold

在reduce的基础上,在buff叠加初始值,结果是1500

 fun foldFlow() {
        viewModelScope.launch {
            val fold = countDownFlow.filter {
                it % 2 == 1
            }.onEach {
                println("The current terminalFlow onEach $it")
            }.fold(100) { accumulator, value ->
                //在 reduce的基础上,再叠上初始值
                accumulator * value //累计乘

            }
            println("The current fold terminalFlow is $fold")

        }

5.flow的 flat操作

fun flatFlow() {
        val flow1 = flow {
            emit(1)
            delay(1000)
            emit(5)
        }

        viewModelScope.launch {
            flow1.flatMapConcat { value ->
                flow {
                    emit(value + 1)
                    delay(1500)
                    emit(value + 2)
                }
            }.collect { value ->
                println("The current value$value")

            }
        }
    }

第一个值是1,然后转换成 2 发送出去,过了一秒转换成3发送出去

第二个值是5,转换成6发送出去,再转换成7发送出去

flatMapConcat就是将流中的数据转换成另外一个流,然后这些流按顺序拼接

拼接之后就是 2,3,6,7

主要特点:

  1. 串联流flatMapConcat 会等到上一个流中的所有元素都被发射后,才会继续处理下一个流。这意味着,它会严格地按顺序将流中的所有元素一个接一个地进行处理和发射。

  2. 顺序性flatMapConcat 会确保处理顺序,只有当前的流全部处理完毕后,才会继续处理下一个流,这点和 flatMapMerge 之类的操作符不同。

如果改成

viewModelScope.launch {
            flow1.flatMapMerge { value ->
                flow {
                    emit(value + 1)
                    delay(1500)
                    emit(value + 2)
                }
            }.collect { value ->
                println("The current value$value")

            }
        }

发送端不会等下游处理完,而是直接发送下一个数据了

发送1,处理后变成2发送出去,这时候下游还没处理完

上游又发送了5,处理后变成6发送出去了,前一个的1变成3刚完成,发送了3

最后5转换成7,再发送。

如果是 flatMapLatest呢上一个没处理完的直接取消

所以我们丢失了3的结果。

6.flow buffer,conflate

按顺序执行的

fun flowDemo(){
        val flow1 = flow {
            emit("Appetize")
            delay(1000)
            emit("main dish")
            delay(500)
            emit("desert")
        }
        viewModelScope.launch {
            flow1.onEach {
                println("Flow:$it is delivered")
            }
                .collect{
                    println("Flow: start eating $it")
                    delay(1500)
                    println("Flow: finished eating $it")
                }
        }


    }

普通的flow没有一个缓冲区,所以只能等前面的数据处理完之后,再发送下一个,但是如果我们给他加一个缓冲区buffer,这样,上游数据的发送不受下游处理速度的影响

buffer

这时候,上游发送的就比较快一些了

conflate

我们把吃的速度再放慢一些

fun flowDemo(){
        val flow1 = flow {
            emit("Appetize")
            delay(1000)
            emit("main dish")
            delay(500)
            emit("desert")
        }
        viewModelScope.launch {
            flow1.onEach {
                println("Flow:$it is delivered")
            }.conflate()
                .collect{
                    println("Flow: start eating $it")
                    delay(2000)
                    println("Flow: finished eating $it")
                }
        }
    }

看下

因为吃的太慢了,主菜到的时候,开胃菜还没吃完,

然后甜品已经到了,开胃菜才吃完,这时候,我们来不及吃主菜

直接从甜品开始吃了,这个就是

conflate

conflate不会等下游,会一直发送数据

下游处理完数据之后,也只能继续处理当前最新的数据,而不管中间是否有遗漏未处理的数据

7.flow combine,zip

combine

两个数据中的任一一个数据变化,都会触发combine

 private val flow1 = (1..10).asFlow().onEach {
        delay(1000)
        println("data:flow1 emit $it")
    }
    private val flow2 = (10..20).asFlow().onEach {
        delay(300)
        println("data:flow2 emit $it")
    }
    private val _numberString = MutableStateFlow<String>("init")
    val numberString = _numberString.asStateFlow()
    fun combineData() {
        flow1.combine(flow2) { data1, data2 ->
            println("data:combine $data1,$data2")
            _numberString.value += "($data1,$data2)\n"
        }.launchIn(viewModelScope)
    }

由于flow2数据发送的比较快,10,11的数据没有匹配的flow1,也直接丢失了,直到有12之后才有匹配的

zip

两边数据都变化后,才触发zip

 //需要两个数据 both
    fun zipData() {
        flow1.zip(flow2) { data1, data2 ->
            println("data:zip $data1,$data2")
            _numberString.value += "($data1,$data2)\n"
        }.launchIn(viewModelScope)
    }

merge

merge的话,就是把两个flow合并

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值