什么是 Kotlin Flow流?
在Kotlin官网中定义中是这么说的:
在协程中,与仅返回单个值的挂起函数相反,Flow可按顺序发出多个值, 它以协程为基础构建,可提供多个值,从概念上来讲,Flow是可通过异步方式进行计算处理的一组数据序列,所发出值的类型必须相同。
总结一下,我们可以将Flow定义为具有多个异步计算值的协程。它是可以异步计算的数据流,用于按顺序发送多个值。 发出的数据必须是同一类型,并且也是按顺序发出的,Flow使用挂起的函数以异步方式消费和生产。
为什么需要Kotlin Flow?
为了使应用程序响应并提高其性能,异步编程起着非常重要的作用,日常开发中,在协程没有出现的岁月里,可以使用RxJava响应式编程框架,它是ReactiveX基于Java的扩展,直到Kotlin的出现和发展。
在Kotlin中,相同的功能以Kotlin Flow API的形式提供。它不仅具备所需的所有运算符和功能,而且还支持挂起函数,这有助于以顺序方式执行异步任务,也让代码变得更加简洁和高效。
如果我们有 RxJava/其他响应式实现,为什么要创建 Flow?
在Flow中,如果我们想转换数据,用一个map操作符即可,这与RxJava有些不同,RxJava具有同步的map和异步的latMapSingle。流程图运算符有一个lambda,它是一个暂停,因此它适用于同步和异步。过滤数据也是如此,Flow只有一个Filter运算符适用于两者。
所以Flow能有效减少了在其它响应式实现中引入的操作符,更加方便简洁,这也是为什么要使用Flow的重要原因。
Kotlin Flow 是如何工作的?
平常我们开发都会使用到网络请求吧,当从服务器请求数据并使用异步编程来处理该数据时,Flow会在后台线程中异步管理该数据,因为某些进程可能会运行更长时间来获取数据。一旦收集器接收并收集了数据,就会使用回收器视图显示数据。
我们已经知道,Flow是一系列值,它使用挂起函数异步生成和使用值。
Flow流由三个实体组成:
- 生产者: 用于发出添加到流中的数据。
- 中介:它可以修改发送到流中的值
- 消费者 : 从流中接收值
Flow builders 的几种类型?
我们平时创建一个Flow流一般提供了4种方式
-
flowOf:它用于一组数据的创建Flow流
flowOf(4, 2, 3, 45, 5) .collect { print(it) }
-
asFlow:这是一个扩展函数,有助于将当前类型转换为Flow流
(1..5).asFlow() .collect { print(it) }
-
flow:这就是官方示例程序标准的创建Flow流的方式
flow { (0..10).forEach { emit(it) } }.collect { print(it) }
-
channelFlow:此构建器使用构建器本身提供的发送元素来创建Flow流。
@OptIn(ExperimentalCoroutinesApi::class) channelFlow { (0..10).forEach { send(it) } }.collect { print(it) }
需要注意的是channelFlow这个Api还是实验性的,所以正式项目环境中很少用到
Kotlin Flow如何实现并发?
我们都知道,Flow是异步的,但是收集器collect和发射器emit是按顺序的方式来工作,怎么说呢?就是当收集器开始收集数据,然后发射器发射出数据,直到没有发射器为止。经常有人说,它就是用同步的代码做异步的事情,这个总结挺到位的。当然,为了实现Flow并发,我们必须从使用单个协程转变为使用多个协程。
Flow对此提供了一些特殊的并发运算符,比如说buffer(),简单使用一个例子
fun main() = runBlocking {
val time = measureTimeMillis {
flow {
for (i in 1..5) {
delay(100)
emit(i)
}
}
.buffer()
.collect {
value ->
delay(300)
println(value)
}
}
println("Collected in $time ms")
}
在上述例子中,所有的 delay 所花费的时间是2000ms,当然这是在没有并发操作的情况下,然而通过buffer操作符并发地执行emit,再顺序地执行collect函数后,所花费的时间在1700ms左右,比预期的花费时间更高效。
那我们使用缓存区buffer的时候会发生什么呢?如下图所示
当收集器处理发出的数据时,下一个发射器开始其过程。它不会等待收集器完成它对先前发出的数据的处理并返回控制权。这是通过在单独的协程中运行收集器和发射器来实现的,这与在单个协程中运行的异步顺序方法不同。在内部它使用通道Channel将数据发送给接收者。
Kotlin Flow 如何进行并行调用?
那有小伙伴说了,如果我有很多个长任务想要同时执行呢,也就是并行,Flow能不能做到呢?答案肯定是YES的,为了并行运行长任务,我们需要使用Flow提供的组合操作符zip,它通过指定的函数将两个流集合的排放组合在一起,并根据此函数的结果为每个组合排放单个项目。
让我们通过示例代码来理解弹珠图:
val flowOne = flowOf(1, 2, 3)
val flowTwo = flowOf("A", "B", "C")
flowOne.zip(flowTwo) {
intValue, stringValue ->
"$