【Kotlin学习】协程的基本概念(3)——Flow

kotlin flow介绍

它是kotlin协程与响应式编程模型结合的产物

flow的使用

flow的基本使用方式

flow能够返回多个异步计算的值

【Kotlin学习】协程的基本概念(3)——Flow_第1张图片

如果熟悉RxJava,可以吧collect()对应subscribe(),而emit()对应onNext()

另外的创建flow方式
1.flowOf()

【Kotlin学习】协程的基本概念(3)——Flow_第2张图片

2.asFlow()

【Kotlin学习】协程的基本概念(3)——Flow_第3张图片

3.channelFlow()

【Kotlin学习】协程的基本概念(3)——Flow_第4张图片

channelFlow builderflow builder是有一定差异的
1.flow是Cold Stream,在没有切换线程的情况下,生产者和消费者是同步非阻塞
2.channel Flow是Hot Stream,channelFlow实现了生产者和消费者异步非阻塞模型

使用flow builder,大致花费1s

【Kotlin学习】协程的基本概念(3)——Flow_第5张图片

【Kotlin学习】协程的基本概念(3)——Flow_第6张图片

使用channelFlow builder

【Kotlin学习】协程的基本概念(3)——Flow_第7张图片

【Kotlin学习】协程的基本概念(3)——Flow_第8张图片

如果flow切换线程的化花费的时间会跟使用channelFlow builder的效果差不多

切换线程

只需要使用flowOn就可以切换线程

【Kotlin学习】协程的基本概念(3)——Flow_第9张图片

【Kotlin学习】协程的基本概念(3)——Flow_第10张图片

collect()指定哪个线程,要看整个flow处于哪个CoroutineScope

!不要用withContext切换flow线程

取消flow

如果flow是在一个挂起函数内被挂起了,那么flow是可以被取消的,否则不能

终端操作符

flow的API类似于java stream的API,它同样拥有Intermediate、Opreations、TerminalOperations

flow的Terminal运算符可以是suspend函数,如collect、single、reduce等,也可以是launchIn运算符,用于在指定CoroutineScope内使用flow

Terminal运算符

1.collect
2.single/first
3.toList/toSet/toCollection
4.count
5.fold/reduce
6.launchIn/produceIn/broadcastIn

flow的生命周期

flow有onStartonCompletion来监听flow的创建和结束

【Kotlin学习】协程的基本概念(3)——Flow_第11张图片

在这里插入图片描述

在Android中使用flow创建网络请求时通过onStart操作符调用loading动画以及网络请求结束后通过onCompletion操作符取消动画,或者做一些日志的打印

flow和RxJava

flow和sequences

每一个flow其内部都是按照顺序执行的,这一点和Sequenece类似。但是flow不会阻塞主线程的运行,而sequence会阻塞主线程的运行

使用flow

【Kotlin学习】协程的基本概念(3)——Flow_第12张图片

【Kotlin学习】协程的基本概念(3)——Flow_第13张图片

使用sequence

【Kotlin学习】协程的基本概念(3)——Flow_第14张图片

【Kotlin学习】协程的基本概念(3)——Flow_第15张图片

flow和RxJava

kotlin协程库参考了RxJava

RxJava2/3 Flow
Single Defered
Maybe Defered
Completable Job
Observable Channel、Flow
Flowable Channel、Flow

1.Cold Stream

flow的代码块只有调用collected()才开始运行,正如RxJava创建的Observables只有调用subscribe()才开始运行一样

2.HotStream

可以借助Kotlin Channel来实现Hot Stream

3.Completion

flow完成(正常或出现异常)时,若需要执行一个操作,可以通过imperativedeclarative来完成

imperative

通过使用try…finally实现

【Kotlin学习】协程的基本概念(3)——Flow_第16张图片

declarative
通过onCompletion()函数实现

【Kotlin学习】协程的基本概念(3)——Flow_第17张图片

onCompleted(借助扩展函数实现)

【Kotlin学习】协程的基本概念(3)——Flow_第18张图片

但当flow异常结束时不会执行onCompleted()

4.Backpressure

它是响应式编程会遇到的问题之一

RxJavaFlowable支持的Backpressure策略包括

1.MISSING

创建的Flowable没有指定背压策略,不会对通过onNext发射的数据做缓存或丢弃处理

2.ERROR

如果放入Flowable的异步缓存池的数据超限了,就会抛出MissingBackpressureException异常

3.BUFFER

Flowable的异步缓存池和Observable的一样,没有固定大小,可以无限制添加数据,不会抛出MissingBackpressureException,但会导致OOM

4.DROP

如果Flowable的异步缓存池满了,就会丢掉将要放入缓存池中的数据

5.LATEST

如果缓存池满了,就会丢掉将要放入缓存池中的数据,这一点和DROP策略一样,不同的是,无论缓存池的状态如何,LATEST策略都会将最后一条数据强行放入缓存池中

flow支持的Backpressure通过挂起函数实现

buffer()对应BUFFER策略

【Kotlin学习】协程的基本概念(3)——Flow_第19张图片

【Kotlin学习】协程的基本概念(3)——Flow_第20张图片

conflate对应LATEST策略

【Kotlin学习】协程的基本概念(3)——Flow_第21张图片
【Kotlin学习】协程的基本概念(3)——Flow_第22张图片

flow的异常处理

flow可以用try…catch来捕捉异常

catch操作符

前面讲过onCompletion操作符,但onCompletion不能捕获异常,只能用于判断是否有异常。catch操作符可以捕获来自上游的异常

【Kotlin学习】协程的基本概念(3)——Flow_第23张图片
在这里插入图片描述

若把onCompletion、catch交换位置,当catch操作符捕获到异常后,不会影响下游

【Kotlin学习】协程的基本概念(3)——Flow_第24张图片
在这里插入图片描述

catch操作符用于实现异常透明化处理,在catch操作符内可以使用throw再次抛出异常,可以使用emit()转换为发射值还可用于其他业务。但catch只是中间操作符,不能捕获下游的异常,但我们可以多次使用catch,还可以借助onEach,把业务逻辑放到onEach中,在onEach之后时catch,再之后是collect

retryretryWhen操作符

如果上游遇到异常并使用了retry操作符,那么retry会让flow最多重试retries指定的次数

【Kotlin学习】协程的基本概念(3)——Flow_第25张图片

【Kotlin学习】协程的基本概念(3)——Flow_第26张图片
retry操作符最终调用的是retryWhen操作符

【Kotlin学习】协程的基本概念(3)——Flow_第27张图片

retryWhen操作符的参数是谓词,当谓词返回true时才会进行重试,谓词还接收一个attempt作为参数,表示尝试的次数,从0开始

flow的线程操作

flowOn和RxJava的observeOn

obServeOn操作符接收一个Scheduler参数,用来指定下游操作符运行在特定的线程调度器Scheduler上。flowOn操作符接收一个CoroutineContext参数,影响的是上游的操作

【Kotlin学习】协程的基本概念(3)——Flow_第28张图片
【Kotlin学习】协程的基本概念(3)——Flow_第29张图片

flow builder和map都会受到flowOn影响,并使用Dispatchers.io线程池
【Kotlin学习】协程的基本概念(3)——Flow_第30张图片【Kotlin学习】协程的基本概念(3)——Flow_第31张图片

flow builder和两个map操作符都会受到两个flowOn的影响,第二个map操作符会切换到指定的线程池

buffer实现并发操作

buffer操作符也可以并发地执行任务,它是除了使用flowOn操作符之外的另一种方式,只是不能显式指定Dispatchers

【Kotlin学习】协程的基本概念(3)——Flow_第32张图片
【Kotlin学习】协程的基本概念(3)——Flow_第33张图片

删除buffer后
【Kotlin学习】协程的基本概念(3)——Flow_第34张图片

并行操作

并发与并行的区别

1.并发:一个处理器同时处理多个任务
2.并行:多个处理器或者多核处理器同时处理多个不同的任务,并行是同时发生的多个并发时间

在flow中可以使用flatMapMerge实现并行

【Kotlin学习】协程的基本概念(3)——Flow_第35张图片

输出的数字为乱序

flow的其他操作符

转换操作符transform

在使用它时可以任意多次调用emit,这时它和map的最大区别

【Kotlin学习】协程的基本概念(3)——Flow_第36张图片

【Kotlin学习】协程的基本概念(3)——Flow_第37张图片

限制大小的操作符take

只取前几个emit发射的值

【Kotlin学习】协程的基本概念(3)——Flow_第38张图片在这里插入图片描述

终端操作符

reduce

类似于kotlin集合的reduce函数,能够对集合进行计算操作

对平方数列求和

【Kotlin学习】协程的基本概念(3)——Flow_第39张图片

fold

也类似于kotlin集合的fold函数,也需要设置初始值

对平方数列求和

【Kotlin学习】协程的基本概念(3)——Flow_第40张图片

reduce和fold

【Kotlin学习】协程的基本概念(3)——Flow_第41张图片
【Kotlin学习】协程的基本概念(3)——Flow_第42张图片

查看两者源码我们可以看到,reduce把第一个元素当作起始值,而fold把我们自己设定的值作为初始值

合并操作符

zip

它可以将两个flow进行合并

【Kotlin学习】协程的基本概念(3)——Flow_第43张图片
【Kotlin学习】协程的基本概念(3)——Flow_第44张图片

它会把flowa中的一个item和flowb中对应的一个item进行合并,即使flowb中的每一个item都使用了delay(),在合并过程中也会等待delay()执行完再进行合并。若a中item个数大于b中个数,新的flow的item个数等于较小的flow的item个数

combine

每次从a发出新的item会将其与folwb最新的item合并

【Kotlin学习】协程的基本概念(3)——Flow_第45张图片
【Kotlin学习】协程的基本概念(3)——Flow_第46张图片

flattenMerge

它不会组合多个flow,而是把它们作为单个流执行

【Kotlin学习】协程的基本概念(3)——Flow_第47张图片
【Kotlin学习】协程的基本概念(3)——Flow_第48张图片【Kotlin学习】协程的基本概念(3)——Flow_第49张图片

扁平化操作符

flatMapConcat

它由mapflattenConcat操作符实现
在这里插入图片描述

在调用它之后,collect函数在收集新值之前会等待flatMapConcat内部的flow完成

【Kotlin学习】协程的基本概念(3)——Flow_第50张图片
【Kotlin学习】协程的基本概念(3)——Flow_第51张图片

flatMapMerge

【Kotlin学习】协程的基本概念(3)——Flow_第52张图片

它顺序调用内部代码块并且并行地执行collect函数

【Kotlin学习】协程的基本概念(3)——Flow_第53张图片
【Kotlin学习】协程的基本概念(3)——Flow_第54张图片

flatMapLatest

当发射了新值之后,上个flow就会被取消

【Kotlin学习】协程的基本概念(3)——Flow_第55张图片【Kotlin学习】协程的基本概念(3)——Flow_第56张图片

你可能感兴趣的:(Kotlin学习,android,学习,kotlin)