美文网首页
kotlin协程3:数据流(kotlin中的多值传递)

kotlin协程3:数据流(kotlin中的多值传递)

作者: 爱你为你做饭 | 来源:发表于2024-03-26 19:28 被阅读0次
    1. kotlin的多值数据结构

      • list/序列:具体可以参考先前的文章,即kotlin提供了一系列的复合数据结构进行多数据存储或者参数传递。且提供了一系列的函数用于复合数据结构的遍历,获取等。


        复合数据解构
        • 若产生数据的逻辑复杂耗时比较大(数据的计算逻辑会卡掉当前线程类似于sleep让线程休眠),可以使用挂起函数借助于协程的实现代替上面的逻辑代码,协程不会卡掉当前线程的执行。


          挂起函数
      • 数据流:和复合数据结构list等一致,数据流用于协程间异步的数据传递,其可以传递多值。
    2. 数据流flow

      • 数据流:如上,和复合数据结构list等一致,数据流用于协程间异步的数据传递,其可以传递多值。


        案例:数据流
        • 数据流类同于协程,数据流构建器,数据流数据的产生和收集都类同于挂起函数,且产生和收集和挂起函数一致,并不会阻塞当前协程的执行。
        • simple 函数不再带有 suspend 标识符.
        • 使用 emit 函数, 从流中 发射(emit) 值.
        • 使用 collect 函数, 从流中 收取(collect) 值.
        • 数据流的数据产生在数据收集的时候才会执行,即数据流是冷的流,单纯的数据产生并不会执行,在其collect调用的时候emit函数体才会执行


          数据流的执行
        • 数据流的取消:等同于协程的取消,withTimeoutOrNull或者根协程的取消都会取消数据流的数据的发射和接受。


          数据流的取消
      • 数据流构建器
        • 等同于协程,数据流也有其对应的构建器,最常见的是flow {}, 还有就是flowOf { } 和 {}.asFlow() (此构建器可以将序列或者集合转换为数据流)构建器。
        • flow { ... } 构建器代码段之内的代码可以挂起.属于挂起函数,可以在协程作用域中使用。
      • 数据流上下文:数据流的收集工作总是会在调用收集函数的协程的上下文中执行.
        • 数据流的上下文保留:数据流的上下文取决于数据流的收集的时候所在的上下文。也就是收集函数调用的所在协程的上下文。


          数据流上下文
        • 数据流的上下文取决于数据接受调用的上下文,即数据的发射上下文和数据接受的上下文必须保持一致,不允许切换上下文,上下文不一致会抛出非法异常。即这也是数据流和协程的一个不同点:协程可以切换上下文但是数据流的发射和接受不可用切换上下文。


          数据流不可用切换上下文
        • flowOf 操作符:使用flowOf操作符可以修改数据流的发射器的上下文,使其和接收器的上下文不一致,看似和上面矛盾其实不然,上面是发射器和接收器在同一个协程中,此操作使得发射器和接收器在不同的协程中。


          flowOf操作符
      • 数据流的异常:数据流的发射器或者中间操作出现异常就会抛出异常,导致数据流的接收器因异常停止执行。下面是几种解决方法:
        • 在数据流的收集器进行异常抓取,即在采集器中try...catach: 需要注意的是此处会抓取数据流的所有的异常,包括发射器,中间操作符以及结束操作符内发生的异常还有收集器内的异常也都会被抓取。


          数据流的异常处理

          备注:需要注意的是kotlin的check函数:不符合条件则会抛出非法状态异常。

          • catch操作符:针对数据流的异常抓取除了上面的语法外,kotlin提供了catch操作符进行异常的抓取,具体总结如下:
            • catch操作符替代了try....catch异常的语法
            • catch操作符仅能抓取catch操作符声明以前的异常,包括发射器及其catch以前的中间操作符的异常。通常不能够抓取采集器的异常。


              catch抓取异常
            • 若想catch操作符也能够抓取采集器的逻辑异常则需要借助于数据流的onEach函数,此时collect没有函数体只是起到启动数据流的发射的作用,具体的采集操作挪到了上面说的onEach函数中去了,且onEach操作符需要在catch的前面声明(还是那句话catch只能抓取其声明前的异常)


              抓取采集器异常
      • 数据流的完成:即数据流的结束,类同于java的代码逻辑的结束,即无论成功还是失败都会走的代码逻辑,和java也一致也是使用关键字finally。
        • 使用finally执行数据流的完成


          finally用法
        • 使用onCompletion执行数据流的完成
          • 类同于catch,也可以使用onCompletion操作符代替finnlly。
          • 和catch不同的是:onCompletion有一个参数为cause:代表当前数据流是否正常完成,若没有正常完成则cause不为空是未完成的异常对象,由此可以判断当前的数据流是否正常完成(数据流正常完成的时候会返回null)。


            onCompletion操作符参数cause的用法
          • onCompletion和catch的另一个不同点是onCompletion 可以抓取数据流所有的异常包括采集器的异常,catch仅能够抓取其声明以前的异常。


            onCompletion的用法
      • 数据流的异常和finally两种使用方式取决于逻辑的使用场景:onCompletion还好,catch需要注意其使用的范围,限于其声明前的发射器和中间操作符,不能处理采集器(通过onEach处理可以处理采集器的异常,但是此种方式不一定能处理所有的场景)。
      • 数据流的发射器的item间隔发射可以通过函数delay指定两个item的发射间隔期,即:(1..3).asFlow().onEach { delay(300) }:两个item间隔300ms发射。
    3. 数据流的操作符(类同于kotlin的集合的中间操作符)

      • 操作符:类似于集合和序列,kotlin为数据流也提供了一系列的操作符,用于数据流的数据的转换等,学过rxjava的对操作符再熟悉不过了,rxjava就是通过一系列的操作符对Observer进行创建转换和接受等逻辑操作。
        • 数据流操作符:接受数据流进行逻辑操作后将转换后的数据流重新发射。
        • 数据流操作符和发射器一样也是冷的,即只有数据流的采集器执行后,发射器启动执行,并按照操作符的执行顺序执行到此操作符后才执行。
          • 多操作符执行顺序是顺序执行,发射器到操作符到采集器,理论上是这样,只有特殊的可能会中间停止,比如filter操作符:不符合条件过滤的数值就不会在传递。


            执行顺序
          • 数据流的收集操作直接在调用结束操作符的协程内工作
        • 数据流的中间操作符不是挂起函数,不能挂起,接受数据然后处理数据返回数据并发射数据,但是其处理函数可以是挂起函数。
        • 数据流中的中间操作符整理如下:
          • map:转换操作符,接受一个数据转换后形成新的数据。


            map操作符
          • filter:过滤操作符,根据给定的过滤条件对接受到数据进行过滤,符合继续传递,不符合停止传递。


            filter操作符
          • distinctUntilChanged:过滤操作符:针对发射器的数据,后一个数据和前一个数据不同才会被接受,可以用于过滤发射器的重复数据(需要注意的是其只是过滤前后两个不同的值,并不是过滤整个数据流的值):


            过滤操作符
          • transform: 变换操作符,和上面两个操作符不同的是,此操作符可以发射多个值,即可以将变换过程中的中间值进行发送,进而也就可以执行更复杂的变换逻辑。


            transform操作符
          • take:限制接受数据的数量的操作符,使用此操作符可以控制采集器需要采集几个数据。


            take操作符
          • rebounce:限流操作符:和take不同,take是直接指定接收器只接受几个值,此操作符是通过时间控制接受到发射器的那几个值,其参数是一个时间,比如1s,则发射器1s间隔内的值不接受,1s后的值才接受,再过1s后的值才接受。


            时间限流操作符

            sample:和rebounce很像,截流操作符,也是时间参数,区别暂时不了解后续再说:


            sample操作符
          • 结束操作符:collect就是数据流的一个结束操作符,特例是一个挂起函数,可以在协程中调用,继承了协程的上下文及其作用域。除了collect外还有其他几个结束操作符:
            • collect:数据流的采集器,开始采集数据,也就是启动数据发射和后续的操作符操作,其函数体处理数据流发射的数据。
            • tolist和toset:将数据流的数据转换为list和集合。
            • first:取数据流的first值并确保数据流发射single值。
            • 使用 reduce 和 fold , 将数据流压缩为单个值.接受一个函数作为参数,通过函数将数据流里面的所有数值综合为一个值(即所有的数据流的值加减乘除,具体取决于给定的函数)。


              reduce操作符
              • fold操作符:和reduce一致,也是将数据流的所有的数据值一块处理,具体的处理取决于给定的函数,不同点是fold可以将数据流的值修改其类型,修改类型后重新计算。即数据流中123三个直 reduce和fold函数都是a+b则其值前面是6后面是字符串123.
              • runningFold:我们不需要一个结果值,而是需要继续操作数据流,和fold的区别是fold是值累加处理后返回,此操作符是单次处理后单次返回。


                runningFold操作符
          • flowOf 操作符:可以为数据流的发射器切换上下文的操作符,具体介绍放到数据流的上下文去整理,去那边参考。
          • buffer:缓存操作符,上面聊到了数据流的执行顺序是发射器到采集器顺序执行,对于许多耗时过多的场景此会消耗很多时间,使用此操作符让数据流的发射器和采集器同时执行,数据发射器数据发射后可以在采集器那边进行缓存。


            普通接收器
            buffer操作符
          • conflation:合并操作符:即在发射器发射数据很快,采集器处理数据慢的场景下,使用合并操作符,会将采集器处理过程中发射器发射过来的数据忽略掉,只是处理采集器处理数据完成时收到的数据。此操作符试情况而定吧!因为要忽略掉一些数据,所以对于所有数据都需要的场景不适合使用此操作符,还是buffer更适用一些。


            合并操作符
          • collectLatest: 最新采集器操作符:也是用在发射器发射数据很快,采集器处理数据慢的场景下,和conflation不同的是此操作符不会合并发射器发射的数据,而是在采集器中正常的接受数据,接收数据后遇到长时间的数据处理逻辑此时会停掉上一个数据的逻辑处理直接处理新接受到的数据,以此类推来新的取消旧的进而最后也就是只有处理了数据流最后的数据。


            取最后值操作符
          • zip:和rxjava一致,将两个数据流组合到一起生成一个新的数据流并逐一发射,是一个两元操作符,对应的是两个数据流,组合方式是其item对应映射,即1对1,2对2,3对3等,组合函数可以通过lambda表达式指定。


            zip操作符
          • Combine:结合操作符:和zip不同,zip是组合操作符,此为结合操作符,也是针对两个数据流,结合方式是:两个数据流发射数据后赶到那一个就是那一个,比如A中123B中abc ,A发射a 此时等到B中a 结合为1a,A中发射2,B中还是a 则结合为2a, 此时B发射了b,由于A中还是2此时结合为2b,以此类推直到两个数据流都最终发射完毕为止。组合方式取决于各自数据流的数据发射方式。


            结合操作符
        • Combine和zip区别:两者都是针对两个数据流的,区别不同是后者有这明确的组合方式就是一对一映射 最终长度取决于短的数据流,后者这不是,后者是取决于数据流的数据发射的方式,赶着谁就是谁。
        • 压平操作符:Flatten:将一个数据流数据平铺后给定一个函数通过函数逻辑(函数可能多次发射值)生成一个新的数据流返回,包括三个操作符:


          flatten
          • flatMapConcat:和zip相似,将参数数据流的每一个值都经过函数处理,即先处理第一个值,处理完在处理第二个值,只到最后一个值(针对其每一个值都会等待函数内的数据流完成)。


            flatMapConcat操作符
          • flatMapMerge:数据流函数中,针对数据流单个值参数先逐一的发射每一个值,然后继续执行到下一个值的发射再逐一的发射每一个值,直到最后函数执行完成。和flatMapConcat的区别是:flatMapConcat以函数的执行优先,则前者以参数数据流的值的发射优先。


            flatMapMerge操作符
          • flatMapLatest:和collectLatest一致,针对参数数据流的值的发射,后一个值的到来后遇到函数中耗时逻辑则会取消上一个值的耗时逻辑重新执行当前值的发射,以此类推到最后也就是仅有最后一个值会完全执行整个函数的逻辑。


            flatMapLatest操作符

      参考文章:
      数据流中的操作符

    相关文章

      网友评论

          本文标题:kotlin协程3:数据流(kotlin中的多值传递)

          本文链接:https://www.haomeiwen.com/subject/tdyxtjtx.html