Kotlin数据流 - - - Flow

一.Flow的特性与使用

1.使用Flow返回多个值

1.名为flow的Flow类型构建器函数。
2.flow{…}构建块中的代码可以挂起。
3.函数simpleFlow不再标有suspend修饰符。
4.使用emit函数发射值。
5.使用collect函数收集值。

Kotlin数据流 - - - Flow_第1张图片

 //通过flow返回多个值     flow函数可以不用挂起
    fun simpleFlow() = flow {
        for (i in 1..3) {
            delay(1000)//挂起
            emit(i)//发射产生一个元素
        }
    }

    //这里用协程写了个异步
    fun testMultipleValue3() = runBlocking {
        simpleFlow().collect { value ->
            print(value)
        }
    }

结果演示:

Kotlin数据流 - - - Flow_第2张图片

2.Flow冷流

 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)
        }
    }

打印结果:

Kotlin数据流 - - - Flow_第3张图片

3.Flow的连续性

1.流的每次单独收集都是按顺序执行的,除非使用特殊操作符。
2.从上游到下游每个过渡操作符都会处理每个发射出的值,然后再交给末端操作符。

 //Flow的连续性(链式调用)
    fun testFlowContinuation() = runBlocking {
        //Flow的构建器
        (1..5).asFlow().filter {
            it % 2 == 0
        }.map {
            "string  $it"  //变换类型
        }.collect {
            println("Collect $it")
        }
    }

结果演示:

Kotlin数据流 - - - Flow_第4张图片

4.Flow的构建器

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) }
    }

5.flow的上下文

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

6.Flow配和协程使用

使用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

7.Flow取消

流采用与协程同样的协作取消。像往常一样,流的收集可以是当流在一个可取消的挂起函数(例如delay)中挂起的时候取消。

1.超时取消

 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")
        }
    }

结果演示:

Kotlin数据流 - - - Flow_第5张图片

 这个流在2.5秒的时候被取消了

2.判断条件取消

 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()
        }

    }

效果演示:

Kotlin数据流 - - - Flow_第6张图片

8.处理Flow背压

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)
        }

二.Flow操作符

1.转换操作符

 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

2.限长操作符

 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

3.末端操作符


    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

三.Flow异常

1.Flow处理异常

1.在接收的时候发生异常

 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

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

2.Flow遇到异常后的处理

1.正常完成

 fun testCompleteInFinally()= runBlocking {
        try {
            simpleFlow().collect { println(it) }
        }finally {
            println("Done")
        }
    }

     fun simpleFlow() =flow {
        emit(3)
        delay(100)
         throw  ActivityNotFoundException()
    }

2.非正常死亡

 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) }
    }

打印信息:

Kotlin数据流 - - - Flow_第7张图片

你可能感兴趣的:(Kotlin协程,kotlin,开发语言,android)