一、过渡操作符
过渡操作符用于将流中的每一个元素转换成另一个元素。
1.1. 转换:transform()
转换操作,它会调用 Flow 的 collect() 函数,然后构建一个新的 Flow,这个操作符是很多操作符的基础。
public inline fun <T, R> Flow<T>.transform(
@BuilderInference crossinline transform: suspend FlowCollector<R>.(value: T) -> Unit
): Flow<R> = flow { // Note: safe flow is used here, because collector is exposed to transform on each operation
collect { value ->
// kludge, without it Unit will be returned and TCE won't kick in, KT-28938
return@collect transform(value)
}
}
可以看到,transform() 函数调用了原 Flow 的 collect() 函数,然后调用传入的 lambda 表达式转换收集到的值。
如果想要继续发射值,需要重新调用 emit() 函数。
使用示例:
runBlocking {
flowOf(1, 2, 3).transform {
emit("transformed $it")
}.collect {
println("Collect: $it")
}
}
运行程序,输出如下:
Collect: transformed 1
Collect: transformed 2
Collect: transformed 3
从 transform() 的源码可以看出,由于 transform() 拦截了 collect() 函数,所以 transform() 函数其实非常灵活,并不是只能用于将收集到的值转换成另一个值。我们完全可以调用多次 emit() 发射多个数据,也可以选择性的发射想要继续发射的数据。
1.2. 映射:map()
映射操作,将一个值映射为另一个值,在响应式编程中非常常见的操作。实际上就是调用了 transform() 操作符,只不过 map() 限制了 transform() 只能将收集到的值转换成另一个值然后继续发射:
源码如下:
public inline fun <T, R> Flow<T>.map(crossinline transform: suspend (value: T) -> R): Flow<R> = transform { value ->
return@transform emit(transform(value))
}
使用示例:
runBlocking {
flowOf(1, 2, 3).map {
"mapped $it"
}.collect {
println("Collect: $it")
}
}
运行程序,输出如下:
Collect: mapped 1
Collect: mapped 2
Collect: mapped 3
二、限长操作符
限长的意思是限制长度,用于选择 Flow 中特定的数据。
比如只取 Flow 的前几个数,或者只取后几个数。Flow 为我们内置了一些筛选条件,开发者也可以自由编写筛选条件。
2.1. 取前几个值:take()
take() 需要传入一个 count,表示取几个数。
runBlocking {
flowOf(1, 2, 3).take(1).collect {
println("Collect: $it")
}
}
运行程序,输出如下:
Collect: 1
2.2. 取满足条件的值:takeWhile()
takeWhile() 需要传入一个 lambda 表达式,只取能够让表达式的返回值为 true 的值。需要注意的是,一旦某个值不满足条件,Flow 会立即结束。
runBlocking {
flowOf(1, 2, 3).takeWhile {
it % 2 == 1
}.collect {
println("Collect: $it")
}
delay(1000)
}
这里我们在 takeWhile() 操作符中判断发射的值是否是奇数。运行程序,输出如下:
Collect: 1
这里之所以没有输出 Collect: 3
, 就是因为 takeWhile() 在遇到偶数 2 的时候,中断了整个 Flow。
2.3. 丢弃前几个值:drop()
drop() 与 take() 对应,也需要传入一个 count 值。
2.4. 丢弃满足条件的值 dropWhile()
dropWhile() 与 takeWhile() 对应,传入一个 lambda 表达式,丢弃能够让表达式的返回值为 true 的值。同样地,需要注意的是,一旦某个值不满足条件,后续的值就都不再会被丢弃。
看一个例子:
runBlocking {
flowOf(1, 2, 1).dropWhile {
it < 2
}.collect {
println("Collect $it")
}
}
运行程序,输出如下:
Collect 2
Collect 1
这里输出了最后一个值:Collect 1
,原因就是 2 已经不满足 it < 2 这个条件了,drop 的工作到 2 就结束了,最后一个 1 不会再被丢弃。
三、小结
本文我们介绍了 Flow 的过渡操作符 transform()、map(),限长操作符 take()、takeWhile()、drop()、dropWhile()。值得注意的是 takeWhile()、dropWhile() 会在条件不满足后立即退出判断,并不是每个值都会被判断。
文末
Kotlin 是一种新型的静态类型编程语言,有超过 60% 的专业 Android 开发者在使用,它有助于提高工作效率、开发者满意度和代码安全性。不仅可以减少常见代码错误,还可以轻松集成到现有应用中。
在这里为了方便大家系统的学习Kotlin,这里特意联合了阿里P7架构师和谷歌技术团队共同整理了一份Kotlin全家桶学习资料(扫码免费领取~)
《高级Kotlin强化实战》
第一章 Kotlin入门教程
- Kotlin 概述
- Kotlin 与 Java 比较
- 巧用 Android Studio
- 认识 Kotlin 基本类型
- 走进 Kotlin 的数组
- 走进 Kotlin 的集合
- 集合问题
- 完整代码
- 基础语法
第二章 Kotlin 实战避坑指南
- 方法入参是常量,不可修改
- 不要 Companion 、INSTANCE ?
- Java 重载,在 Kotlin 中怎么巧妙过渡一下?
- Kotlin 中的判空姿势
- Kotlin 复写 Java 父类中的方法
- Kotlin “狠”起来,连TODO 都不放过!
- is、as` 中的坑
- Kotlin 中的 Property 的理解
- also 关键字
- takeIf 关键字
- takeIf 关键字
- 单例模式的写法
第三章 项目实战《Kotlin Jetpack 实战》
- 从一个膜拜大神的 Demo 开始
- Kotlin 写 Gradle 脚本是一种什么体验?
- Kotlin 编程的三重境界
- Kotlin 高阶函数
- Kotlin 泛型
- Kotlin 扩展
- Kotlin 委托
- 协程“不为人知”的调试技巧
- 图解协程:suspend