在上一节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 :默认的启动模式,协程创建之后,立刻开始调度,在调度之前如果被取消,那么就直接进入取消响应状态
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,但是后续的代码不会执行
LAZY :这个启动模式就是延迟创建协程,只要协程需要的时候才会创建执行;如果在调度之前就被取消,那么就会进入异常结束状态
这种方式是常用的启动模式,在有需要的时候,例如launch的start或者join,async的await等,不需要立刻创建协程而是按需懒加载
那么只要是在执行前取消了,肯定就不会执行
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的生命周期
看下图
当一个协程创建之后,进入 Active 的状态,在执行调度之前,如果取消了这个协程,那么就进入 Cancelling 的状态,任务取消之后,就是 Cancelled 状态;
如果没有取消,那么协程体执行的时候,就是 Completing 的状态,如果其中出现的错误异常,执行失败,也会进入Cancelling的状态;
当协程执行完成之后,就会进入 Completed 状态,这个时候,isCancelled = true,会进入 Cancalled 状态