Kotlin协程的那些事 ---- 协程的启动模式和生命周期

在上一节Kotlin协程的那些事 ---- 初识协程中,主要介绍了协程的一些概念性的东西,本节继续了解协程中的一些概念

协程的概念

1 协程的启动模式

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

拿launch来看,在协程的构造方法中,除了CoroutineScope协程作用域之外,CoroutineStart代表协程的启动模式

DEFAULT :默认的启动模式,协程创建之后,立刻开始调度,在调度之前如果被取消,那么就直接进入取消响应状态
Kotlin协程的那些事 ---- 协程的启动模式和生命周期
t1和t2时间段是协程创建时间,协程创建完成之后,立刻进入调度的状态,执行协程体内的代码,那么在t1和t2时间段内是可以取消协程的,那么取消之后协程体内的代码就不会执行

suspend fun test(){

    viewModelScope.launch {
        delay(3*1000)
        Log.e("TAG","第一个launch任务")
    }

    val launch2 = viewModelScope.launch {
        delay(1*1000)
        Log.e("TAG","第二个launch任务")
    }

    Log.e("TAG","第二个launch任务 被取消")
    launch2.cancel()

}

E/TAG: 第二个launch任务 被取消
E/TAG: 第一个launch任务

ATOMIC : 协程创建之后,立刻开始调度,在进入到第一个挂起点之前,不响应取消操作

suspend fun test(){

    viewModelScope.launch {
        delay(3*1000)
        Log.e("TAG","第一个launch任务")
    }

    val launch2 = viewModelScope.launch(
        start = CoroutineStart.ATOMIC
    ) {
        Log.e("TAG","协程第一个挂起点")
        delay(1*1000)
        Log.e("TAG","第二个launch任务")
    }

    Log.e("TAG","第二个launch任务 被取消")
    launch2.cancel()

}

E/TAG: 第二个launch任务 被取消
E/TAG: 协程第一个挂起点
E/TAG: 第一个launch任务

其中launch2任务中,第一个挂起点是delay,那么在执行delay之前,取消操作是不管用的,还是会执行delay,但是后续的代码不会执行
Kotlin协程的那些事 ---- 协程的启动模式和生命周期
LAZY :这个启动模式就是延迟创建协程,只要协程需要的时候才会创建执行;如果在调度之前就被取消,那么就会进入异常结束状态

这种方式是常用的启动模式,在有需要的时候,例如launch的start或者join,async的await等,不需要立刻创建协程而是按需懒加载
Kotlin协程的那些事 ---- 协程的启动模式和生命周期
那么只要是在执行前取消了,肯定就不会执行

UNDISPATCHED:顾名思义,就是不调度了;创建协程之后,立刻当前函数的调用栈中执行

正常情况下,协程会在调度器分配的线程中执行,例如下面的示例中,协程域是在IO线程中执行

val launch2 = viewModelScope.launch(
    Dispatchers.IO,
    start = CoroutineStart.LAZY
) {
    Log.e("TAG", Thread.currentThread().name)
    delay(1*1000)
    Log.e("TAG","第二个launch任务")
}

Log.e("TAG","第二个launch任务 被取消")
launch2.join()

E/TAG: DefaultDispatcher-worker-2
切换启动模式为 UNDISPATCHED 后
E/TAG: main

如果启动模式是 UNDISPATCHED ,那么就不分配了,因为是在主线程创建的协程,那么就在主线程中执行这个协程体,是不安全的

2 协程作用域构建器 coroutineScope 和 supervisorScope

因为CoroutineScope是协程作用域构建器,可以创建一个协程作用域,那么其实就可以不用系统自带的viewModelScope之类的,可以自定义一些协程创建方式,但需要自己管理生命周期

suspend fun getUser():MutableLiveData<Int>{
    //这种需要自己去管理生命周期
    coroutineScope {
        liveData.value = Repository.getUser()
    }

    return liveData
}

CoroutineScope也是一个挂起函数,其中包含多个协程,其中每个协程之间息息相关,如果其中某个协程出现异常,那么其他的协程也不会执行

coroutineScope {

    async {
        delay(3*1000)
        Log.e("TAG","delay 3s")
    }

    async {
        delay(1*1000)
        Log.e("TAG","delay 1s")
        throw IOException()
    }
}

 E/TAG: delay 1s
 
然后应用直接崩掉

但是如果使用supervisorScope作用域构建器,各个协程之间是互不干扰的,一个协程的失败不会影响其他的协程

supervisorScope {

    async {
        delay(3*1000)
        Log.e("TAG","delay 3s")
    }

    async {
        delay(1*1000)
        Log.e("TAG","delay 1s")
        throw IOException()
    }
}

 E/TAG: delay 1s
 E/TAG: delay 3s

这个虽然有个协程抛了异常,但是应用没有崩掉,而且其他协程正常执行

3 Job的生命周期

看下图
Kotlin协程的那些事 ---- 协程的启动模式和生命周期
当一个协程创建之后,进入 Active 的状态,在执行调度之前,如果取消了这个协程,那么就进入 Cancelling 的状态,任务取消之后,就是 Cancelled 状态;

如果没有取消,那么协程体执行的时候,就是 Completing 的状态,如果其中出现的错误异常,执行失败,也会进入Cancelling的状态;

当协程执行完成之后,就会进入 Completed 状态,这个时候,isCancelled = true,会进入 Cancalled 状态

上一篇:JQuery使用教程


下一篇:CF1088F Ehab and a weird weight formula