前言
翻译自 进阶Part 1: Coroutines。
CoroutineScope
一个CoroutineScope跟踪任意一个用launch或async创建的协程(Coroutines),运行中的协程可以在任何时间点用scope.cancel()取消。
当你在APP的一个特定层想要开始并控制协程的生命周期的时候应该创建一个CoroutineScope。这里有一些KTX库,已经在某些生命周期类中提供了CoroutineScope,例如viewmodelScope和lifecyclescope。
创建CoroutineScope需要传一个CoroutineContext类型的参数,例如如下代码
val scope = CoroutineScope(Dispatchers.Default)
val job = scope.launch {
// 这里创建协程
}
作者这里是这样创建的
val scope = CoroutineScope(Job() + Dispatchers.Main)
val job = scope.launch {
// 这里创建协程
}
这个+号的作用后续会讲。这也是传入一个CoroutineContext类型的参数创建CoroutineScope。
Job
Job是协程的句柄,对于每一个通过launch或者async创建的协程,都会返回一个job对象,唯一指定协程病管理它的生命周期。如上面的代码,我们也可以将job传给CoroutineScope掌控其生命周期。
CoroutineContext
CoroutineContext是一套定义协程行为的元素。它构成于:
- job -控制协程的生命周期
- CoroutineDispatcher - 将工作分配到合适的线程
- CoroutineName - 协程名字,debug很有用
- CoroutineExceptionHandler - 处理未捕获的异常,在后续会讲解。
新协程的CoroutineContext是什么?我们已经知道将创建一个新的job对象使我们管理协程的生命周期,剩下的几个元素会从CoroutineContext的父级(另一个协程或者在其创建的CoroutineScope)中继承。
由于CoroutineScope可以创建协程,并且可以在一个协程中创建更多协程,因此一个隐藏的任务层级将会被创建,下面举个例子
val scope = CoroutineScope(Job() + Dispatchers.Main)
val job = scope.launch {
// 创建新的协程, CoroutineScope作为父级
val result = async {
// 创建新的协程,通过launch创建的协程作为父级
}.await()
}
协程在任务层次结构中执行,CoroutineContext是继承的,父级可以来自CoroutineScope或另一个协程,继承的根节点一般是CoroutineScope。
大白话就是,每一个协程都有一套定义其行为的元素,这些元素由上面四个组成,总称为CoroutineContext 。新协程的这套元素来自其他协程或者创建出来的CoroutineScope 。协程中可以创建协程,这套元素也是继承的,由父级传下来的,一般根节点是创建出来的CoroutineScope。
Job lifecycle
一个job贯穿一系列状态:new、active、completing、completed、cancelling和cancelled 。我们可以通过isActive 、isCancelled 和 isCompleted得到job的状态属性。
job生命周期
父CoroutineContext 释义
在任务继承体系中,任意一个协程都有一个父级可以是一个CoroutineScope 或者是其他的协程。然而,协程的最终父CoroutineContext可以不同于父协程的CoroutineContext,
计算规则如下:
Parent context = Defaults + inherited CoroutineContext + arguments
- Defaults:一些元素有默认值,例如CoroutineDispatcher的默认值是Dispatchers.Default ,CoroutineName的默认值是“ coroutine”
- inherited CoroutineContext 是创建它的CoroutineScope或协程的CoroutineContext
- arguments 在构建协程的传的参数将优先于继承的那些元素
注意: CoroutineContexts可以通过+号合并。由于CoroutineContext是一系列元素,创建一个新的CoroutineContext,+号右边的元素会覆盖加好左边的元素。
例如:(Dispatchers.Main, “name”) + (Dispatchers.IO) = (Dispatchers.IO, “name”)。
每个通过CoroutineScope发起的协程,CoroutineContext至少会有这些元素。协程名字之所以是灰色的是因为来自默认值
我们已经知道一个新协程的父CoroutineContext是什么,那么新协程自己的真正CoroutineContext是:
New coroutine context = parent CoroutineContext + Job()
如果用上面的图片中的scope创建一个新的协程,例如
val job = scope.launch(Dispatchers.IO) {
// new coroutine
}
这个协程的父CoroutineContext和它真正的CoroutineContext是什么呢?如下
父CoroutineContext和新协程的CoroutineContext的job永远都不会是同一个实例,因为新协程总会得到一个job的新实例
父CoroutineContext的真正结果之所以有Dispatchers.IO而不是scope的CoroutineDispatcher,是因为被创建协程时传的Dispatchers.IO这个参数重写了。另外,检查父CoroutineContext的job是scope的job实例(红色),新协程的CoroutineContext的job则是新的实例(绿色)。
后续会学到CoroutineScope的CoroutineContext可以是不同的job实现类,例如SupervisorJob, 这将改变CoroutineScope处理异常的方式。因此,使用该scope创建的协程,父级job将会是SupervisorJob。但是,当协程的父级是另一个协程时,父级Job始终为Job类型。
后记
至此,part 1 正式翻译完了, 望指正o( ̄︶ ̄)o。
网友评论