它是kotlin协程与响应式编程模型结合的产物
flow能够返回多个异步计算的值
如果熟悉RxJava,可以吧collect()对应subscribe(),而emit()对应onNext()
另外的创建flow方式
1.flowOf()
2.asFlow()
3.channelFlow()
channelFlow builder
跟flow builder
是有一定差异的
1.flow是Cold Stream
,在没有切换线程的情况下,生产者和消费者是同步非阻塞的
2.channel Flow是Hot Stream
,channelFlow实现了生产者和消费者异步非阻塞模型
使用flow builder,大致花费1s
使用channelFlow builder
如果flow切换线程的化花费的时间会跟使用channelFlow builder的效果差不多
只需要使用flowOn
就可以切换线程
collect()指定哪个线程,要看整个flow处于哪个CoroutineScope
下
!不要用withContext
切换flow线程
如果flow是在一个挂起函数内被挂起了,那么flow是可以被取消的,否则不能
flow的API类似于java stream的API,它同样拥有Intermediate、Opreations、TerminalOperations
flow的Terminal运算符可以是suspend函数,如collect、single、reduce等,也可以是launchIn运算符,用于在指定CoroutineScope内使用flow
1.collect
2.single/first
3.toList/toSet/toCollection
4.count
5.fold/reduce
6.launchIn/produceIn/broadcastIn
flow有onStart
、onCompletion
来监听flow的创建和结束
在Android中使用flow创建网络请求时通过onStart操作符调用loading动画以及网络请求结束后通过onCompletion操作符取消动画,或者做一些日志的打印
每一个flow其内部都是按照顺序执行的,这一点和Sequenece类似。但是flow不会阻塞主线程的运行,而sequence会阻塞主线程的运行
使用flow
使用sequence
kotlin协程库参考了RxJava
RxJava2/3 | Flow |
---|---|
Single | Defered |
Maybe | Defered |
Completable | Job |
Observable | Channel、Flow |
Flowable | Channel、Flow |
flow的代码块只有调用collected()
才开始运行,正如RxJava创建的Observables只有调用subscribe()才开始运行一样
可以借助Kotlin Channel
来实现Hot Stream
flow完成(正常或出现异常)时,若需要执行一个操作,可以通过imperative
和declarative
来完成
通过使用try…finally实现
declarative
通过onCompletion()
函数实现
onCompleted
(借助扩展函数实现)
但当flow异常结束时不会执行onCompleted()
Backpressure
它是响应式编程会遇到的问题之一
RxJavaFlowable支持的Backpressure策略包括
创建的Flowable没有指定背压策略,不会对通过onNext发射的数据做缓存或丢弃处理
如果放入Flowable的异步缓存池的数据超限了,就会抛出MissingBackpressureException异常
Flowable的异步缓存池和Observable的一样,没有固定大小,可以无限制添加数据,不会抛出MissingBackpressureException,但会导致OOM
如果Flowable的异步缓存池满了,就会丢掉将要放入缓存池中的数据
如果缓存池满了,就会丢掉将要放入缓存池中的数据,这一点和DROP策略一样,不同的是,无论缓存池的状态如何,LATEST策略都会将最后一条数据强行放入缓存池中
buffer()
对应BUFFER策略
conflate
对应LATEST策略
flow可以用try…catch来捕捉异常
前面讲过onCompletion操作符,但onCompletion不能捕获异常,只能用于判断是否有异常。catch操作符可以捕获来自上游的异常
若把onCompletion、catch交换位置,当catch操作符捕获到异常后,不会影响下游
catch操作符用于实现异常透明化处理,在catch操作符内可以使用throw再次抛出异常,可以使用emit()转换为发射值还可用于其他业务。但catch只是中间操作符,不能捕获下游的异常,但我们可以多次使用catch,还可以借助onEach,把业务逻辑放到onEach中,在onEach之后时catch,再之后是collect
retry
、retryWhen
操作符如果上游遇到异常并使用了retry操作符,那么retry会让flow最多重试retries指定的次数
retryWhen操作符的参数是谓词,当谓词返回true时才会进行重试,谓词还接收一个attempt
作为参数,表示尝试的次数,从0
开始
obServeOn操作符接收一个Scheduler参数,用来指定下游操作符运行在特定的线程调度器Scheduler上。flowOn操作符接收一个CoroutineContext参数,影响的是上游的操作
flow builder和map都会受到flowOn影响,并使用Dispatchers.io线程池
flow builder和两个map操作符都会受到两个flowOn的影响,第二个map操作符会切换到指定的线程池
buffer操作符也可以并发地执行任务,它是除了使用flowOn操作符之外的另一种方式,只是不能显式指定Dispatchers
1.并发:一个处理器同时处理多个任务
2.并行:多个处理器或者多核处理器同时处理多个不同的任务,并行是同时发生的多个并发时间
在flow中可以使用flatMapMerge
实现并行
输出的数字为乱序
transform
在使用它时可以任意多次调用emit,这时它和map的最大区别
take
只取前几个emit发射的值
reduce
类似于kotlin集合的reduce函数,能够对集合进行计算操作
对平方数列求和
fold
也类似于kotlin集合的fold函数,也需要设置初始值
对平方数列求和
reduce和fold
查看两者源码我们可以看到,reduce把第一个元素当作起始值,而fold把我们自己设定的值作为初始值
zip
它可以将两个flow进行合并
它会把flowa中的一个item和flowb中对应的一个item进行合并,即使flowb中的每一个item都使用了delay(),在合并过程中也会等待delay()
执行完再进行合并。若a中item个数大于b中个数,新的flow的item个数等于较小的flow的item个数
combine
每次从a发出新的item会将其与folwb最新的item合并
flattenMerge
它不会组合多个flow,而是把它们作为单个流执行
flatMapConcat
在调用它之后,collect函数在收集新值之前会等待flatMapConcat内部的flow
完成
flatMapMerge
它顺序调用内部代码块并且并行地执行collect函数
flatMapLatest
当发射了新值之后,上个flow就会被取消