侧边栏壁纸
博主头像
Z同学博主等级

工作磨平激情前,坚持技术的热忱。 欢迎光临Z同学的技术小站。 分享最新的互联网知识。

  • 累计撰写 274 篇文章
  • 累计创建 55 个标签
  • 累计收到 74 条评论

Kotlin 协程 Flow的基础介绍一

Z同学
2022-01-12 / 0 评论 / 2 点赞 / 283 阅读 / 3,579 字
温馨提示:
本文最后更新于 2022-01-17,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1.介绍

我们在协程中处理的时候,往往面临函数的异步返回,如果异步返回多个值,我们该如何处理这些数据值之间的计算呢?

在这种情况下,Flow 流就是我们最佳解决方案了。所以,本篇主要学习Flow的使用。

希望能够让大家了解Flow是什么。

2.Flow 介绍

2.1 Flow 的初级使用

示例:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
fun simple(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100)//阻塞100毫秒
        emit(i) //发送数据值给流
    }
}

fun main() = runBlocking<Unit> {
    launch {
        for (k in 1..3) {
            println(" 我没有被锁定$k")
            delay(100) //阻塞100毫秒
        }
    }
    simple().collect { value -> println(value) }
}
//输出
 我没有被锁定1
1
 我没有被锁定2
2
 我没有被锁定3
3

结论:

  1. flow{...} 代码块中的代码可以被挂起。
  2. 上面的函数simple 不需要suspend 修饰符就可以使用。
  3. Flow使用 emit 函数将结果值发送出去。简称:发射值。
  4. Flow使用 collect 函数收集发送的值。简称:收集值。

2.2 Flow 流是冷的

Flow 是一种类似序列的冷流,意思就是说流里面的代码并不是实时的,而是直到流被collect函数进行收集的时候才会运行

所以说流是冷的。

2.3 Flow的取消基础

Flow 采用和协程一样的取消模式。流的数据的收集可以在当流在一个可取消的挂起函数(delay)中被暂停挂起时。

可以进行取消操作。

示例:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow

fun simple(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100)
        println("发射 $i")
        emit(i)
    }
}

fun main() = runBlocking<Unit> {
    withTimeoutOrNull(250) { // 在 250 毫秒后超时
        simple().collect { value -> println(value) }
    }
    println("结束")
}
//输出
发射 1
1
发射 2
2
结束

我们通过输出的结果就可以看到,Flow 流并没有能够输出到最后值3 ,而是在2之后就被关闭取消了。

2.4 Flow的构建器

我们在上面的Flow的初级使用中,flow{...}就是Flow的一个构建器,而且是一个最基础的构建器。

还有其他的几种构建器声明方式:

  • flow{...} :基础构建器。发射值我们可以自定义。
  • flowOf:定义了一个发射固定值的流。
  • asFlow():扩展方式,可以将各种集合和序列转换为流。

asFlow示例:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect


fun main() = runBlocking<Unit> {
    withTimeoutOrNull(250) { // 在 250 毫秒后超时
        (1..3).asFlow().collect { value -> println(value) }
    }
    println("结束")
}
//输出
1
2
3
结束

3. Flow 操作符

Flow使用中,我们可以使用操作符转换流,就像使用集合与序列一样。

3.1 过渡流操作符

应用于上游流,并返回下游流。这个操作符本身不是挂起函数,所以它的运行速度比较快,返回新的转换流的定义。

操作符为 mapfilter等。但是Flow使用这些操作符合集合等序列对象的区别在于,可以调用挂起函数。

示例:一个请求中的Flow可以使用map操作符映射出结果,即使执行一个长时间的请求操作也可以使用挂起函数。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map


suspend fun performRequest(request: Int): String {
    delay(1000) // 模仿长时间运行的异步工作
    return "返回值: $request"
}

fun main() = runBlocking<Unit> {
    (1..3).asFlow() // 一个请求流
        .map { request -> performRequest(request) }
        .collect { response -> println(response) }
}
//输出
返回值: 1
返回值: 2
返回值: 3

每隔一秒打印一个返回值。

3.1.1 转换操作符-transform

Flow转换操作符中最通用的一种是transform。它可以用来模仿简单的转换,我们可以用它来发射任意值,任意次。

示例:在执行长时间运行的异步请求之前,发射一个字符串并跟踪响应。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.transform


suspend fun performRequest(request: Int): String {
    delay(1000) // 模仿长时间运行的异步工作
    return "返回值: $request"
}

fun main() = runBlocking<Unit> {
    (1..3).asFlow() // 一个请求流
        .transform { request ->
            emit("提出请求 $request")
            emit(performRequest(request))
        }
        .collect { response -> println(response) }
}
//输出
提出请求 1
返回值: 1
提出请求 2
返回值: 2
提出请求 3
返回值: 3

3.1.2 限长操作符-take

通常使用限长操作符在Flow触及相应限制的时候,将执行取消。

示例:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*


fun numbers(): Flow<Int> = flow {
    try {
        emit(1)
        emit(2)
        println("这一行将不会被执行")
        emit(3)
    } finally {
        println("返回最后的Number值")
    }
}

fun main() = runBlocking<Unit> {
    numbers()
        .take(2) // 只获取前两个
        .collect { value -> println(value) }
}
//输出
1
2
返回最后的Number值

上面的例子中通过take限制了获取长度。而超过该限制,会执行协程的取消。该模式下协程的取消操作是通过抛出异常来执行的,也就是我们上面例子中的try ... finally

3.2 末端流操作符

是指在Flow上用于启动流收集的挂起函数,例如collect就是最基础的末端操作符

还有其他的末端操作符例如:

  • toListtoSet 转换各种数据集合。
  • first 获取第一个值与确保Flow发射单个值的操作符等。
  • 使用reducefoldFlow约定到单个值

示例:

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

fun main() = runBlocking<Unit> {
    val sum = (1..5).asFlow()
        .map { it * it } // 数字 1 至 5 的平方
        .reduce { a, b -> a + b } // 求和(末端操作符)
    println(sum)
}
//输出
55

关于Flow 的信息一篇介绍不完,我们下篇继续。

2

评论区