android kotlin 协程(一)

config:

  • system: macOS

  • android studio: 2022.1.1 Electric Eel

  • gradle: gradle-7.5-bin.zip

  • android build gradle: 7.1.0

  • Kotlin coroutine core: 1.6.4

前语:最近体系的学习了一遍协程, 计划经过10篇左右blog来记载一下我对协程的了解, 从最简略的 runBlocking开端; 到最终 suspend和continuation的关系等等

tips:前面几篇全都是协程的根本运用,没有源码,等后面对协程有个根本了解之后,才会简略的分析一下源码!

学习我这个系列的协程, 只需求记住一点, suspend函数 永久不会堵塞main线程履行! 永久是异步的!

看完本篇你将会学到哪些知识:

  • runBlocking()
  • CoroutineScope#launch()
  • CoroutineScope#async()
  • Job的常用办法
  • 协程状况[isActive,isCancelled,isCompleted]

runBlocking

定义: runBlocking 会堵塞线程来等候自己子协程履行完, 而且关于不是子协程的效果域,也会尽量的去履行,

首要来了解一下什么是自己的子协程

android kotlin 协程(一) 基础入门

通常咱们经过

  • CoroutineScope.launch{}
  • CoroutineScope.async{}

来敞开一个协程,由于当时是在CoroutineScope效果域中,所以直接launch / async 即可

这段代码能够看出,runBlocking 会等候子协程全部履行完,然后在结束任务,由于协程都是异步的,

所以会先履行协程之外的代码,然后再履行协程中的代码

能够在协程中增加一些睡觉操作再来测验一下

android kotlin 协程(一) 基础入门

能够看出,仍是能够正常的履行完所有代码

现在解释完了定义中的前半句话: runBlocking 会堵塞线程来等候自己子协程履行完, 而且关于不是子协程的效果域,也会尽量的去履行,

再来看一下后半句话:

android kotlin 协程(一) 基础入门

能够看出,经过自定义coroutine 和 GlobalScope,来创建的协程照样能够履行出来

那么在他们之中略微增加一点逻辑会怎么样?

android kotlin 协程(一) 基础入门

能够看出,一旦增加了一点逻辑, runBlocking是不会等候非子协程的效果域

假如想让runBlocking等候非子协程履行完,那么只需求调用Job.#join() 即可

例如这样:

android kotlin 协程(一) 基础入门

join()办法目前能够了解为: 等候当时协程履行完 在往下履行其他代码,

一旦调用了join()办法,那么协程就变成了同步的,那么这块代码总共履行需求4s

由于协程1并没有join, 所以协程1仍是异步的,

协程2调用了join,所以在履行协程2的过程中,协程1也在履行.

所以协程1,与协程2的履行时间为2s

android kotlin 协程(一) 基础入门

tips: 在开发中不建议运用runBlocking,由于会堵塞主线程,堵塞主线程的时间,用来子协程的履行..

敞开协程两种不同的办法

在上面代码中咱们提到了,敞开协程有2种办法

  • CoroutineScope#launch{}
  • CoroutineScope#async{}

先来看相同点:

android kotlin 协程(一) 基础入门

相同点便是无论是哪种办法,都会履行里面的代码

那么这两种办法有什么区别呢?

  • launch无法回来数据, async能够回来成果

    android kotlin 协程(一) 基础入门

回来的成果经过 Deferred#await() 来获取,而且调用Deferred#await()的时分,会等候async{} 履行完结之后在往下履行,就和Job#join相同,不过await()有回来成果

运用await的时分有一个留意点:

android kotlin 协程(一) 基础入门

那么也能够看到,launch{} 与 async{} 的回来值也有所不同:

  • launch{} 回来 Job
  • async{} 回来Deferred

其实实质上来说,async 回来的也是Job,不过仅仅Job的子类Deferred而已,Deferred仅仅对回来值等一些操作的封装

android kotlin 协程(一) 基础入门

那么Job是用来干什么的呢?

Job是用来办理协程的生命周期的, 例如刚才提到的 Job.join() 就能够让协程 “当即履行”

launch{} 和 async{} 捕获反常的办法也不同,这个等下一篇专门聊反常的时分在具体讲解

Job.cancel 撤销协程

协程比线程好用的一点便是协程能够自己办理生命周期, 而线程则不能够

android kotlin 协程(一) 基础入门

这里需求留意的是,假如协程体中还在履行,可是外部现已撤销了,那么则会throw反常出来

JobCancellationException

fun main() = runBlocking<Unit> {
    println("main start")
    val job = launch {
        try {
            (0..100).forEachIndexed { index, _ ->
                delay(1000)
                println("launch $index")
            }
        } catch (e: Exception) {
            println("协程被撤销了 $e")
        }
    }
    // 协程履行完结监听
    job.invokeOnCompletion {
        println("协程履行结束${it}")
    }
    delay(5500)
    // 撤销协程
    job.cancel()
    println("main end")
}

Job#invokeOnCompletion: 协程完结回调

运行成果:

main start
launch 0
launch 1
launch 2
launch 3
launch 4
main end
协程被撤销了 kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#2":StandaloneCoroutine{Cancelling}@76a4d6c
协程履行结束kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#2":StandaloneCoroutine{Cancelled}@76a4d6c

coroutine的3种状况

coroutine的三种状况都是经过Job来办理的:

  • isActive 是否是活泼状况
  • isCancelled 是否撤销
  • isCompleted 是否完结

先来看看正常流程履行的代码:

android kotlin 协程(一) 基础入门

咱们知道协程始终是异步履行的,在履行printlnJob的时分,协程体中的代码还没有真实的履行

所以此刻处于活泼状况,而且协程没有被履行完

假如咱们在协程履行完结的回调中调用

android kotlin 协程(一) 基础入门

那么此刻,协程体中的代码现已履行完了,那么此刻便是非活泼状况

还剩一个Job#isCancelled 这个办法比较简略,简略的说便是是否调用了Job.cancel()

android kotlin 协程(一) 基础入门

可是这里有一个特别奇怪的点,明明现已调用Job#cancel() 来撤销协程,而且协程体中的代码也没履行,可是为什么还显现协程没有履行完呢?

由于Job#cancel() 并不是suspend函数,不是suspend函数就没有康复功能,这行文字可能看的有一点迷惑,先不必管什么挂起于康复,现在只需求知道

咱们调用cancel() 的时分会紧跟着一个,Job#join() 即可

或许直接调用Job.cancelAndJoin() 即可

android kotlin 协程(一) 基础入门

挂起康复,这4个字我了解了10天左右,不可能经过本篇就讲清楚,现在只需求会调用这些api,即可!!

那么问题就来了,这个状况有什么用呢?

先来看一段代码:

能够惊讶的发现,这段代码无论怎么都cancel不掉.好像是失效了相同

那么处理这个问题,就能够检测协程是否是活泼状况,例如这样

android kotlin 协程(一) 基础入门

Job也提供了一个办法: Job#ensureActive()

ensureActive() 实质也是经过isActive判别,不同的是,当撤销的时分能够捕获到撤销的反常,然后来处理对应的事件

图片地址: gitee.com/lanyangyang…

回忆一下本篇:

本篇咱们讲解了runBlocking, 这个函数会帮咱们堵塞主线程, 堵塞住线程的时分会等候内部的子协程全部履行完

还聊了最基础的怎么敞开一个协程, launch / async 以及他们的相同点和不同点

最终引出了协程生命周期办理者Job, 讲解了Job常用的办法,以及job的3种状况

办法名 效果 补充
join() 当即康复协程体履行 等候协程体履行完结,在履行后续代码
cancel() 撤销协程 ,假如撤销时,协程体还在履行,这throw JobCancellationException,这个反常不会上报,会自行处理
invokeOnCompletion() 协程体履行完结回调
isActive 协程体是否是活泼状况
isCancelled 协程体是否被撤销
isCompleted 协程体是否履行完结

完好代码

下一篇预告:

  • CoroutineDispatcher // 协程调度器 用来切换线程

  • CoroutineName // 协程姓名

  • CoroutineStart // 协程启动形式

  • CoroutineException // launch / async 捕获反常

  • GlobalCoroutineException // 全局捕获反常

原创不易,您的点赞便是我最大的支撑!