持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
Flow 异步流
挂起函数可以异步的返回单个值,但是该如何异步返回多个计算好的值呢?这正是 Kotlin 流(Flow)的用武之地。
异步返回多个值的方案
- 集合
fun simple(): List<Int> = listOf(1, 2, 3)
fun main() {
simple().forEach { value -> println(value) }
}
-
序列
如果使用一些消耗 CPU 资源的阻塞代码计算数字(每次计算需要 100 毫秒)那么我们可以使用 Sequence 来表示数字
fun simple(): Sequence<Int> = sequence { // 序列构建器
for (i in 1..3) {
Thread.sleep(100) // 假装我们正在计算
yield(i) // 产生下一个值
}
}
fun main() {
simple().forEach { value -> println(value) }
}
-
挂起函数
当这些值由异步代码计算时,我们可以使用 suspend 修饰符标记函数 simple, 这样它就可以在不阻塞的情况下执行其工作并将结果作为列表返回
suspend fun simple(): List<Int> {
delay(1000) // 假装我们在这里做了一些异步的事情
return listOf(1, 2, 3)
}
fun main() = runBlocking<Unit> {
simple().forEach { value -> println(value) }
}
-
Flow
使用 List 结果类型,意味着我们只能一次返回所有值。 为了表示异步计算的值流(stream),我们可以使用 Flow 类型(正如同步计算值会使用 Sequence 类型)
fun simple(): Flow<Int> = flow { // 流构建器
for (i in 1..3) {
delay(100) // 假装我们在这里做了一些有用的事情
emit(i) // 发送下一个值
}
}
fun main() = runBlocking<Unit> {
// 启动并发的协程以验证主线程并未阻塞
launch {
for (k in 1..3) {
println("I'm not blocked $k")
delay(100)
}
}
// 收集这个流
simple().collect { value -> println(value) }
}
Flow与其他方式的区别
-
名为 flow 的 Flow 类型构建器函数。
-
flow { ... } 构建块中的代码可以挂起。
-
函数 simple 不再标有 suspend 修饰符。
-
流使用 emit函数 发射值。
-
流使用 collect 函数收集值。
Flow 应用
Android当中,文件下载就是Flow的一个非常典型的应用
冷流
Flow 是一种类似于序列的冷流 flow构建器中的代码直到流被收集的时候才运行。
fun simple(): Flow<Int> = flow {
println("Flow started")
for (i in 1..3) {
delay(100)
emit(i)
}
}
fun main() = runBlocking<Unit> {
println("Calling simple function...")
val flow = simple()
println("Calling collect...")
flow.collect { value -> println(value) }
println("Calling collect again...")
flow.collect { value -> println(value) }
}
流的连续性
- 流的每次单独收集都是按顺序执行的,除非使用特殊操作符
- 从上游到下游每个过渡操作符都会处理每个发射出的值,然后交给末端操作符
(1..3).asFlow() // 一个请求流
.map { request -> performRequest(request) }
.collect { response -> println(response) }
流构建器
- flowOf构建器定义了一个发射固定值的流
- 使用.asFlow()扩展函数,可以将各种集合与序列转换为流
(1..3).asFlow().collect { value -> println(value) }
流上下文
-
流的收集总是在调用协程的上下文中发生,流的该属性称为上下文保存
-
flow{...}构建器中的代码必须遵循上下文保存属性,并且不允许从其他上下文中发射
-
flowOn操作符,该函数用于更改流发射的上下文