1.名为flow的Flow类型构建器函数。
2.flow{…}构建块中的代码可以挂起。
3.函数simpleFlow不再标有suspend修饰符。
4.使用emit函数发射值。
5.使用collect函数收集值。
//通过flow返回多个值 flow函数可以不用挂起
fun simpleFlow() = flow {
for (i in 1..3) {
delay(1000)//挂起
emit(i)//发射产生一个元素
}
}
//这里用协程写了个异步
fun testMultipleValue3() = runBlocking {
simpleFlow().collect { value ->
print(value)
}
}
结果演示:
flow是一种类似与序列的冷流,flow构建器中的代码直到流被手机的时候才运行。
fun simpleFlow02() = flow {
println("Flow started")
for (i in 1..3) {
delay(1000)
emit(i)
}
}
//冷流 只有调用collect的时候Flow才会收集元素
fun testFlowIsCold() = runBlocking {
val simpleFlow02 = simpleFlow02()
println("Calling collect...")
simpleFlow02.collect { value ->
println(value)
}
println("Calling collect again...")
simpleFlow02.collect { value ->
println(value)
}
}
打印结果:
1.流的每次单独收集都是按顺序执行的,除非使用特殊操作符。
2.从上游到下游每个过渡操作符都会处理每个发射出的值,然后再交给末端操作符。
//Flow的连续性(链式调用)
fun testFlowContinuation() = runBlocking {
//Flow的构建器
(1..5).asFlow().filter {
it % 2 == 0
}.map {
"string $it" //变换类型
}.collect {
println("Collect $it")
}
}
结果演示:
1.fIowOf构建器定义了一个发射固定值集的流。
2.使用.asFlow()扩展函数,可以将各种集合与序列转换为流。
//Flow的构建器 flowOf asFlow
fun testFlowBuilder() = runBlocking {
flowOf("one", "two")
//onEach 返回一个流,该流在上游流的每个值在下游发出之前调用给定的操作。
.onEach {
delay(1000)
}.collect {
println(it)
}
listOf(1,2,3).asFlow().collect { value -> println(value) }
}
1.流的收集总是在调用协程的上下文中发生,流的该属性称为上下文保存。
2.flow{...}构建器中的代码必须遵循上下文保存属性,并且不允许从其他上下文中发射(emit).
3.fIowOn操作符,该函数用于更改流发射的上下文。
//改变flow的上下文
fun simpleFlow05() = flow {
println("Flow started ${Thread.currentThread().name}")
for (i in 1..5) {
emit(i)
}
}.flowOn(Dispatchers.Default)
fun testFlowChangeContext() = runBlocking {
simpleFlow05().collect { value ->
println(
"Collected $value ${
Thread.currentThread().name
}"
)
}
println("")
}
打印结果:
Flow started DefaultDispatcher-worker-1
Collected 1 main
Collected 2 main
Collected 3 main
Collected 4 main
Collected 5 main
使用launchIn替换collect我们可以再单独的协程中启动流的收集
//flow事件
fun events() = (1..3).asFlow().onEach {
delay(100)
}
//flow流配合协程使用
fun testFlowLaunch() = runBlocking {
var job = events().onEach { event ->
println("Event: $event ${Thread.currentThread().name}")
}.launchIn(
CoroutineScope(Dispatchers.IO)
)
delay(1000)
job.cancelAndJoin()
}
打印结果:
Event: 1 DefaultDispatcher-worker-1
Event: 2 DefaultDispatcher-worker-1
Event: 3 DefaultDispatcher-worker-1
流采用与协程同样的协作取消。像往常一样,流的收集可以是当流在一个可取消的挂起函数(例如delay)中挂起的时候取消。
fun simpleFlow06() = flow {
for (i in 1..5) {
emit(i)
delay(1000)
println("Emitting $i")
}
}
//超时取消
fun testCancelFlow() = runBlocking {
withTimeoutOrNull(2500) {
simpleFlow06().collect { value ->
println(value)
}
println("Done")
}
}
结果演示:
这个流在2.5秒的时候被取消了
fun simpleFlow06() = flow {
for (i in 1..5) {
emit(i)
delay(1000)
println("Emitting $i")
}
}
fun testCancelFlowCheck() = runBlocking {
//这种情况由于元素都是经由emit函数发射出来的可以用cancel取消
/* simpleFlow06().collect {
value ->
print(value)
if (value==3) cancel()
}
}*/
//另一种情况
//cancellable明确要取消,每搜集一个元素都会判断是否达成取消条件
(1..5).asFlow().cancellable().collect { value ->
println(value)
if (value == 3) cancel()
}
}
效果演示:
1.buffer(),并发运行流中发射元素的代码。
2.conflate(),合并发射项,不对每个值进行处理。
3.collectLatest(),取消并重新发射最后一个值。
4.当必须更改CoroutineDispatcher时,到flowOn操作符使用了相同的缓冲机制,但是buffer函数显式地请求缓冲而不改变执行上下文。
fun simpleFlow07() = flow {
for (i in 1..5) {
delay(100)
emit(i)
println("Emitting $i")
}
}
fun testBackPressure()= runBlocking {
//这里由于发生的时间比收集的快吗,但只有收集后才能算结束,所以发射加搜集所需时间加起来了
val measureTimeMillis = measureTimeMillis {
simpleFlow07()
//切换线程由于本方法和simpleFlow07函数不是在同一个线程里,所以发射可以并行发射又由于本身自带缓冲,而这里切换到了主线程会按顺序收集已发射的元素
//.flowOn(Dispatchers.Default)
//.buffer(50)//缓存可以由此并发运行流中的代码
//.conflate()//发射元素合并,注意这个会缺少元素
//.collectLatest {}//这个自会收集最后一个
.collect { value ->
delay(300)
println(value)
}
}
println(measureTimeMillis)
}
suspend fun performRequest(request: Int):String{
delay(1000)
return "response $request"
}
//修改类型操作符
fun testTransformFlowOperator()= runBlocking {
//每个修改一次类型
/* (1..3).asFlow()
.map { value ->
performRequest(value)
}.collect {
println(it)
}*/
(1..3).asFlow()
//transform()每个元素修改多次类型
.transform { value ->
emit("Making request $value")
emit(performRequest(value))
}.collect {
println(it)
}
}
打印结果:
Making request 1
response 1
Making request 2
response 2
Making request 3
response 3
fun numbers()= flow {
try {
emit(1)
emit(2)
println("This line will not execute")
emit(3)
}finally {
println("Finally in numbers")
}
}
fun testLimitLengthOperator()= runBlocking {
//限长操作符 take(): 限制输出长度
numbers().take(2).collect {value -> println(value) }
}
打印结果:
1
2
Finally in numbers
fun testTerminalOperator()= runBlocking {
//map 返回函数运算的结果
//reduce 从第一个元素开始累加值,并对当前累加器值和每个元素应用操作
val sum=(1..5).asFlow().map { it*it }.reduce{
//这里把1到5的平方相加
a,b->a+b
}
//只有一个元素的时候可以single
val single = flowOf(1).single()
//获取第一个元素
val first = flowOf(1..6).first()
//先集合中的元素进行运算,最后再与fold的参数运算
val sum1=(1..5).asFlow().fold(3){
a,b->a+b
}
println(sum)
println(sum1)
}
打印结果:
55
1
1..6
18
4.组合操作符
//zip()合并操作符
fun testZip()= runBlocking {
val asFlow = (1..3).asFlow().onEach { delay(300) }
val flowOf = flowOf("one", "Two", "Three").onEach { delay(400) }
val currentTimeMillis = System.currentTimeMillis()
//这里打印出来了合并所需时间,证明整个过程是异步的(400m)
asFlow.zip(flowOf){a,b->"$a $b"}.collect { value -> println("$value at ${System.currentTimeMillis()-currentTimeMillis}") }
}
fun requestFlow(i:Int)= flow {
emit("$i: First")
delay(500)
emit("$i: Second")
}
结果打印:
1 one at 435
2 Two at 844
3 Three at 1255
5.展平流
流表示异步接收的值序列,所以很容易遇到这样的情况:每个值都会触发对另一个值序列的请求,然而,由于流具有异步的性质,因此需要不同的展平模式,为此,存在一系列的流展平操作符:
1.flatMapConcat连接模式.
2.flatMapMerge合并模式
3.flatMapLatest最新展平模式
//当一个flow里包裹着另一个flow比如flow>,我需要用展平流来转成一flow
fun estFlatMapConcat()= runBlocking {
val startTime=System.currentTimeMillis()
(1..3).asFlow().onEach { delay(100) }
//flatMapLatest{}
//flatMapMerge前面效果与flatMapConcat一样顺序不一样
// .flatMapMerge { }
.flatMapConcat{
//这里调用map的话requestFlow返回值是flow>,所以调用flatMapConcat会自己解开一层 打印结果
requestFlow(it)
}.collect {
value -> println("$value at ${System.currentTimeMillis()-startTime}")
}
}
flatMapLatest打印结果:
1: First at 149
2: First at 266
3: First at 370
3: Second at 882
flatMapMerge打印结果:
//1: First at 143
//2: First at 240
//3: First at 347
//1: Second at 655
//2: Second at 748
//3: Second at 860
flatMapConcat打印结果:
1: First at 129
1: Second at 641
2: First at 749
2: Second at 1257
3: First at 1367
3: Second at 1874
fun simpleFlow08()= flow {
for (i in 1..4){
println("Emitting $i")
emit(i)
}
}
fun testFlowException()= runBlocking {
try {
simpleFlow08().collect {value ->
println(value)
check(value <=1 ){
"Collected $value"
}
}
}catch (e:Throwable){
println("Caught $e")
}
}
打印结果:
Emitting 1
1
Emitting 2
2
fun testFlowException2()= runBlocking {
flow {
emit(1)
throw ArithmeticException("Div 0")
//上游有异常,在保证不打破flow涉及原则的情况下推荐使用catch函数
}.catch { e:Throwable-> println("Caught $e")
emit(10) //也可以这么写
}
.flowOn(Dispatchers.IO)
.collect {
println(it)
}
}
打印结果:
Caught java.lang.ArithmeticException: Div 0
1
10
fun testCompleteInFinally()= runBlocking {
try {
simpleFlow().collect { println(it) }
}finally {
println("Done")
}
}
fun simpleFlow() =flow {
emit(3)
delay(100)
throw ActivityNotFoundException()
}
fun simpleFlow() = flow {
emit(3)
delay(100)
throw ActivityNotFoundException()
}
fun testCompleteInCompletion() = runBlocking {
/* simpleFlow().onCompletion { exception -> println("Flow completed $exception") }
.catch { e -> println(e) }
.collect {
println(it)
/* check(
it<=1){"Collected $it"}*/
}*/
//非正常死亡时他会拿到异常信息 正常结束后也会执行
simpleFlow().onCompletion { println("Done") }.collect { println(it) }
}
打印信息: