Koltin Flow 基础详解
kotlinx-coroutines-core 1.4.2版本出来了,没有了之前的实验方法警告,终于可以可以愉快的玩耍了,现在准备把有关flow的相关资料整理汇总一下
1.概览
2.Kotlin Flow 介绍
A cold asynchronous data stream that sequentially emits values and completes normally or with an exception。
意思是:按顺序发出值并正常完成或异常完成的Cold异步数据流。
与rxjava作用类似,可能会在以后的开发中逐步代替rxjava,使整个开发生态更加趋向一体化
4.Flow的创建
- Empty Flow
emptyFlow<String>()
- 通过flowOf函数
flowOf(1, 2, 3)
// 1, 2, 3
flowOf(listOf(1,2,3))
// [1, 2, 3]
- Iterable调用asFlow函数
listOf(1, 2, 3).asFlow()
// 1, 2, 3
- 无参但是有返回值的函数**(() -> T)**调用asFlow函数
fun flowBuilderFunction(): Int {
return 10
}
::flowBuilderFunction.asFlow()
// 10
- 无参但是有返回值的挂起函数**(() -> T)**调用asFlow函数
suspend fun flowBuilderFunction(): Int {
return 10
}
::flowBuilderFunction.asFlow()
// 10
- Array调用asFlow函数
LongRange(1, 5).asFlow().collect { value -> println(value) }
5.Flow操作符
Delay相关的操作符
-
debounce
特性:
- 如果两个相邻的值生产出来的时间间隔超过了[timeout]毫秒,就忽过滤掉前一个值 最后一个值不受影响,总是会被释放emit。 [timeout]可以传毫秒,也可以传Duration
flow { emit(1) delay(3000) emit(2) delay(1000) emit(3) delay(1000) emit(4) }.debounce(2000) // 结果:1 4 // 解释: // 2和1的间隔大于2000,1被释放 // 3和2的间隔小于2000, 2被忽略 // 4和3的间隔小于2000, 3被忽略 // 4是最后一个值不受timeout值的影响, 4被释放 flow { emit(1) delay(3000) emit(2) delay(1000) emit(3) delay(1000) emit(4) }.debounce(2000.milliseconds) // 结果:1 4 应用:可用于搜索框的反复输入内容筛选
Distinct相关的操作符
-
distinctUntilChanged
1.如果生产的值和上个发送的值相同,值就会被过滤掉
flow { emit(1) emit(1) emit(2) emit(2) emit(3) emit(4) }.distinctUntilChanged() // 结果:1 2 3 4 // 解释: // 第一个1被释放 // 第二个1由于和第一个1相同,被过滤掉 // 第一个2被释放 // 第二个2由于和第一个2相同,被过滤掉 // 第一个3被释放 // 第一个4被释放- 可以传参(old: T, new: T) -> Boolean,进行自定义的比较
private class Person(val age: Int, val name: String) flow { emit(Person(20, "张三")) emit(Person(21, "李四")) emit(Person(21, "王五")) emit(Person(22, "赵六")) }.distinctUntilChanged{old, new -> old.age == new.age } .collect{ value -> println(value.name) } // 结果:张三 李四 赵六 // 解释:本例子定义如果年龄相同就认为是相同的值,所以王五被过滤掉了- 可以用distinctUntilChangedBy转换成年龄进行对比
flow { emit(Person(20, "张三")) emit(Person(21, "李四")) emit(Person(21, "王五")) emit(Person(22, "赵六")) }.distinctUntilChangedBy { person -> person.age } // 结果:张三 李四 赵六
Emitters相关的操作符
-
transform
对每个值进行转换
flow { emit(1) emit(2) emit(3) emit(4) }.transform { if (it % 2 == 0) { emit(it * it) } } // 结果:4 16 // 解释: // 1 不是偶数,被忽略 // 2 是偶数,2的平方4 // 3 不是偶数,被忽略 // 4 是偶数,4的平方16 -
onStart
第一个值被释放之前被执行
flow { emit(1) emit(2) emit(3) emit(4) }.onStart { emit(1000) } // 结果:1000 1 2 3 4 // 解释: // 第一个值1被释放的时候调用了emit(10 00), 所以1000在1之前被释放 -
onCompletion
最后一个值释放完成之后被执行
flow { emit(1) emit(2) emit(3) emit(4) }.onCompletion { emit(1000) } // 结果:1 2 3 4 1000 // 解释: // 第一个值4被释放的时候调用了emit(100 0), 所以1000在4之后被释放
Limit相关的操作符
-
drop
忽略最开始的[count]个值
flow { emit(1) emit(2) emit(3) emit(4) }.drop(2) // 结果:3 4 // 解释: // 最开始释放的两个值(1,2)被忽略了 -
dropWhile
判断第一个值如果满足(T) -> Boolean这个条件就忽略
flow { emit(1) emit(2) emit(3) emit(4) }.dropWhile { it % 2 == 0 } // 结果:1 2 3 4 // 解释: // 第一个值不是偶数,所以1被释放 flow { emit(1) emit(2) emit(3) emit(4) }.dropWhile { it % 2 != 0 } // 结果:2 3 4 // 解释: // 第一个值是偶数,所以1被忽略 -
take
只释放前面[count]个值
flow { emit(1) emit(2) emit(3) emit(4) }.take(2) // 结果:1 2 // 解释: // 前面两个值被释放 -
takeWhile
判断第一个值如果满足(T) -> Boolean这个条件就释放
flow { emit(1) emit(2) emit(3) emit(4) }.takeWhile { it%2 != 0 } // 结果:1 // 解释: // 第一个值满足是奇数条件 flow { emit(1) emit(2) emit(3) emit(4) }.takeWhile { it%2 == 0 } // 结果:无 // 解释: // 第一个值不满足是奇数条件
CoroutineContext相关的操作符
-
flowOn
可以切换CoroutineContext 说明:flowOn只影响该运算符之前的CoroutineContext,对它之后的CoroutineContext没有任何影响
-
buffer
将flow的多个任务分配到不同的协程中去执行,加快执行的速度。
-
conflate
如果值的生产速度大于值的消耗速度,就忽略掉中间未来得及处理的值,只处理最新的值。
val flow1 = flow {
delay(2000)
emit(1)
delay(2000)
emit(2)
delay(2000)
emit(3)
delay(2000)
emit(4)
}.conflate()
flow1.collect { value ->
println(value)
delay(5000)
}
// 结果: 1 3 4
// 解释:
// 2000毫秒后生产了1这个值,交由collect 执行,花费了5000毫秒,当1这个值执行co llect完成后已经经过了7000毫秒。
// 这7000毫秒中,生产了2,但是collect还 没执行完成又生产了3,所以7000毫秒以后 会直接执行3的collect方法,忽略了2这 个值
// collect执行完3后,还有一个4,继续执 行。
Flatten相关的操作符
- flatMapConcat
将原始的Flow<T>通过[transform]转换成Flow<Flow<T>>,然后将Flow<Flow<T>>释放的Flow<T>其中释放的值一个个释放。
flow {
delay(1000)
emit(1)
delay(1000)
emit(2)
delay(1000)
emit(3)
delay(1000)
emit(4)
}.flatMapConcat {
flow {
emit("$it 产生第一个flow值")
delay(2500)
emit("$it 产生第二个flow值")
}
}.collect { value ->
println(value)
}
// 结果
// I/System.out: 1 产生第一个flow值
// I/System.out: 1 产生第二个flow值
// I/System.out: 2 产生第一个flow值
// I/System.out: 2 产生第二个flow值
// I/System.out: 3 产生第一个flow值
// I/System.out: 3 产生第二个flow值
// I/System.out: 4 产生第一个flow值
// I/System.out: 4 产生第二个flow值
// 解释:
// 原始Flow<Int>通过flatMapConcat被转换成Flow<Flow<Int>>
// 原始Flow<Int>首先释放1,接着Flow<Flow<Int>> 就会释放 1产生第一个flow值 和 1产生第二个flow值 两个值
// Flow<Int>释放2,...
// Flow<Int>释放3,...
// Flow<Int>释放4,...
-
flattenConcat
和flatMapConcat类似,只是少了一步Map操作。
flow { delay(1000) emit(flow { emit("1 产生第一个flow值") delay(2000) emit("1 产生第二个flow值") }) delay(1000) emit(flow { emit("2 产生第一个flow值") delay(2000) emit("3 产生第二个flow值") }) delay(1000) emit(flow { emit("3 产生第一个flow值") delay(2000) emit("3 产生第二个flow值") }) delay(1000) emit(flow { emit("4 产生第一个flow值") delay(2500) emit("4 产生第二个flow值") }) }.flattenConcat() // 结果 // I/System.out: 1 产生第一个flow值 // I/System.out: 1 产生第二个flow值 // I/System.out: 2 产生第一个flow值 // I/System.out: 2 产生第二个flow值 // I/System.out: 3 产生第一个flow值 // I/System.out: 3 产生第二个flow值 // I/System.out: 4 产生第一个flow值 // I/System.out: 4 产生第二个flow值 -
flatMapMerge
将原始的Flow通过[transform]转换成Flow<Flow>,然后将Flow<Flow>释放的Flow其中释放的值一个个释放。 它与flatMapConcat的区别是:Flow<Flow>释放的Flow其中释放的值没有顺序性,谁先产生谁先释放。
flow {
delay(1000)
emit(1)
delay(1000)
emit(2)
delay(1000)
emit(3)
delay(1000)
emit(4)
}.flatMapMerge {
flow {
emit("$it 产生第一个flow值")
delay(2500)
emit("$it 产生第二个flow值")
}
}.collect { value ->
println(value)
}
-
merge
将Iterable<Flow>合并成一个Flow
val flow1 = listOf(
flow {
emit(1)
delay(500)
emit(2)
},
flow {
emit(3)
delay(500)
emit(4)
},
flow {
emit(5)
delay(500)
emit(6)
}
)
flow1.merge().collect { value -> println("$value") }
// 结果: 1 3 5 2 4 6
// 解释:
// 按Iterable的顺序和耗时顺序依次释放值
-
transformLatest
原始flow会触发transformLatest转换后的flow, 当原始flow有新的值释放后,transformLatest转换后的flow会被取消,接着触发新的转换后的flow
-
flatMapLatest
和transformLatest类似, 原始flow会触发transformLatest转换后的flow, 当原始flow有新的值释放后,transformLatest转换后的flow会被取消,接着触发新的转换后的flow
区别:flatMapLatest的transform转换成的是Flow, transformLatest的transform转换成的是Unit
-
mapLatest
和transformLatest类似, 原始flow会触发transformLatest转换后的flow, 当原始flow有新的值释放后,transformLatest转换后的flow会被取消,接着触发新的转换后的flow
区别:mapLatest的transform转换成的是T,flatMapLatest的transform转换成的是Flow,transformLatest的transform转换成的是Unit
Transform相关的操作符
-
filter
通过predicate进行过滤,满足条件则被释放
flow { emit(1) emit(2) emit(3) emit(4) }.filter { it % 2 == 0 } // 结果: 2 4 // 解释: // 2和4满足it % 2 == 0,被释放 -
filterNot
通过predicate进行过滤,不满足条件则被释放
flow { emit(1) emit(2) emit(3) emit(4) }.filterNot { it % 2 == 0 } // 结果: 1 3 // 解释: // 1和3不满足it % 2 == 0,被释放 -
filterIsInstance
如果是某个数据类型则被释放
flow { emit(1) emit("2") emit("3") emit(4) }.filterIsInstance<String>() // 结果: "2" "3" // 解释: // "2" "3"是String类型,被释放 -
filterNotNull
如果数据是非空,则被释放
flow { emit(1) emit("2") emit("3") emit(null) }.filterNotNull() // 结果: 1 "2" "3" -
map
将一个值转换成另外一个值
flow { emit(1) emit(2) emit(3) emit(4) }.map { it * it } // 结果: 1 4 9 16 // 解释: // 将1,2,3,4转换成对应的平方数 -
mapNotNull
将一个非空值转换成另外一个值
-
withIndex
将值封装成IndexedValue对象
flow { emit(1) emit(2) emit(3) emit(4) }.withIndex() // 结果: // I/System.out: IndexedValue(index=0, value=1) // I/System.out: IndexedValue(index=1, value=2) // I/System.out: IndexedValue(index=2, value=3) // I/System.out: IndexedValue(index=3, value=4) -
onEach
每个值释放的时候可以执行的一段代码
-
scan
有一个初始值,然后每个值都和初始值进行运算,然后这个值作为后一个值的初始值
flow { emit(1) emit(2) emit(3) emit(4) }.scan(100) { acc, value -> acc * value } // 结果: 100 100 200 600 2400 // 解释: // 初始值 100 // 1 100 * 1 = 100 // 2 100 * 2 = 200 // 3 200 * 3 = 600 // 4 600 * 4 = 2400 -
runningReduce
和scan类似,但是没有初始值,最开始是它本身
flow { emit(1) emit(2) emit(3) emit(4) }.runningReduce { acc, value -> acc * value } // 结果: 1 2 6 24 // 解释: // 1 1 // 2 1 * 2 = 2 // 3 2 * 3 = 6 // 4 6 * 4 = 24
合并的操作符
-
zip
将两个Flow在回调函数中进行处理返回一个新的值 R 当两个flow的长度不等时只发送最短长度的事件
val nums = (1..4).asFlow() val strs = flowOf("one", "two", "three") nums.zip(strs) { a, b -> "$a -> $b" } .collect { println(it) } // 结果: 1 -> one 2 -> two 3 -> three -
combine
任意一个flow释放值且都有释放值后会调用combine后的代码块,且值为每个flow的最新值。 和zip的区别: 组合两个流,在经过第一次发射以后,任意方有新数据来的时候就可以发射,另一方有可能是已经发射过的数据
val flow1 = flowOf(1, 2, 3, 4).onEach { delay(10) } val flow2 = flowOf("a", "b", "c", "d").onEach { delay(20) } flow1.combine(flow2) { first, second -> "$first$second" }.collect { println("$it") } // 结果:1a 2a 2b 3b 4b 4c 4d // 解释: // 开始 --- flow1 释放 1,flow2 释放 a, 释放1a // 10毫秒 --- flow1 释放 2,释放2a // 20毫秒 --- flow2 释放 b,此时释放2b // 30毫秒 --- flow1 释放 3,此时释放3b // 40毫秒 --- flow1 释放 4,此时释放4b // 40毫秒 --- flow2 释放 c,此时释放4c // 60毫秒 --- flow2 释放 d,此时释放4d
retry相关操作符
- retry
public fun <T> Flow<T>.retry( retries: Long = Long.MAX_VALUE, // 重试次数 predicate: suspend (cause: Throwable) -> Boolean = { true } ): Flow<T>
- retryWhen
public fun <T> Flow<T>.retryWhen(
predicate: suspend FlowCollector<T>.(cause: Throwable, attempt: Long) -> Boolean
): Flow<T>
末端操作符
-
Collect相关的末端操作符
-
collect
接收值
-
launchIn
scope.launch { flow.collect() }的缩写, 代表在某个协程上下文环境中去接收释放的值
val flow1 = flow { delay(1000) emit(1) delay(1000) emit(2) delay(1000) emit(3) delay(1000) emit(4) } flow1.onEach { println("$it") } .launchIn(GlobalScope) // 结果:1 2 3 4 -
collectIndexed
和withIndex对应的,接收封装的IndexedValue
val flow1 = flow { emit(1) emit(2) emit(3) emit(4) }.withIndex() flow1.collectIndexed { index, value -> println("index = $index, value = $value") } // 结果: // I/System.out: index = 0, value = IndexedValue(index=0, value=1) // I/System.out: index = 1, value = IndexedValue(index=1, value=2) // I/System.out: index = 2, value = IndexedValue(index=2, value=3) // I/System.out: index = 3, value = IndexedValue(index=3, value=4) -
collectLatest
collectLatest与collect的区别是,如果有新的值释放,上一个值的操作如果没执行完则将会被取消
val flow1 = flow { emit(1) delay(1000) emit(2) delay(1000) emit(3) delay(2000) emit(4) } flow1.collectLatest { println("正在计算收到的值 $it") delay(1500) println("收到的值 $it") } // 结果: // I/System.out: 正在计算收到的值 1 // I/System.out: 正在计算收到的值 2 // I/System.out: 正在计算收到的值 3 // I/System.out: 收到的值 3 // I/System.out: 正在计算收到的值 4 // I/System.out: 收到的值 4 // 解释: // 1间隔1000毫秒后释放2,2间隔1000毫秒后释放3,这间隔小于需要接收的时间1500毫秒,所以当2和3 到来后,之前的操作被取消了。 // 3和4 之间的间隔够长能够等待执行完毕,4是最后一个值也能执行
-
-
Collection相关的末端操作符
-
toList
将释放的值转换成List
flow { emit(1) delay(1000) emit(2) delay(1000) emit(3) delay(2000) emit(4) } println(flow1.toList()) // 结果:[1, 2, 3, 4] -
toSet
将释放的值转换成Set
flow { emit(1) delay(1000) emit(2) delay(1000) emit(3) delay(2000) emit(4) } println(flow1.toSet()) // 结果:[1, 2, 3, 4]
-
-
Count相关的末端操作符
-
count
1.计算释放值的个数
val flow1 = flow { emit(1) delay(1000) emit(2) delay(1000) emit(3) delay(2000) emit(4) } println(flow1.count()) // 结果:4 2.计算满足某一条件的释放值的个数 val flow1 = flow { emit(1) delay(1000) emit(2) delay(1000) emit(3) delay(2000) emit(4) } println(flow1.count { it % 2 == 0 }) // 结果:2 // 解释: // 偶数有2个值 2 4 ``` -
-
Reduce相关的末端操作符
-
reduce
和runningReduce类似,但是只计算最后的结果。
val flow1 = flow { emit(1) emit(2) emit(3) emit(4) } println(flow1.reduce { acc, value -> acc * value }) // 结果:24 // 解释:计算最后的结果,1 * 2 * 3 * 4 = 24 -
fold
和scan类似,有一个初始值,但是只计算最后的结果。
val flow1 = flow { emit(1) emit(2) emit(3) emit(4) } println(flow1.fold(100) { acc, value -> acc * value }) // 结果:2400 // 解释:计算最后的结果,100 * 1 * 2 * 3 * 4 = 2400 -
single
只接收一个值的Flow 注意:多于1个或者没有值都会报错
val flow1 = flow { emit(1) } println(flow1.single()) // 结果:1 -
singleOrNull
接收一个值的Flow或者一个空值的Flow
-
first/firstOrNull
- 接收释放的第一个值/接收第一个值或者空值
val flow1 = flow { emit(1) emit(2) emit(3) emit(4) } println(flow1.first()) // 结果:1 ``` 2. 接收第一个满足某个条件的值 val flow1 = flow { emit(1) emit(2) emit(3) emit(4) } println(flow1.first { it % 2 == 0}) // 结果:2 ``` -
Flow的错误异常处理
-
可以通过 try catch 捕获错误异常
try { flow { for (i in 1..3) { emit(i) } }.collect { println("接收值 $it") check(it <= 1) { "$it 大于1" } } } catch (e: Throwable) { println("收到了异常: $e") } // 结果: // I/System.out: 接收值 1 // I/System.out: 接收值 2 // I/System.out: 收到了异常: java.lang.IllegalStateException: 2 大于1 // 解释: // 收到2的时候就抛出了异常,让后flow被取消,异常被捕获 -
通过catch函数
catch函数能够捕获之前产生的异常,之后的异常无法捕获。
flow { for (i in 1..3) { emit(i) } }.map { check(it <= 1) { "$it 大于1" } it } .catch { e -> println("Caught $e") } .collect() // 结果: // Caught java.lang.IllegalStateException: 2 大于1
Flow的取消
-
CoroutineScope.cancel
GlobalScope.launch { val flow1 = flow { for(i in 1..4){ emit(i) } } flow1.collect { value -> println("$value") if (value >= 3) { cancel() } } } // 结果:1 2 3 -
流取消检测
在协程处于繁忙循环的情况下,必须明确检测是否取消。 可以添加 .onEach { currentCoroutineContext().ensureActive() }, 但是这里提供了一个现成的 cancellable 操作符来执行此操作:
(1..5).asFlow().cancellable().collect { value -> if (value == 3) cancel() println(value) }