前言
Kotlin
是一种在Java
虚拟机上运行的静态类型编程语言,被称之为Android
世界的Swift
,在Google
I/O2017中,Google
宣布Kotlin
成为Android
官方开发语言
Job
当我们创建一个协程时,会返回一个Job
对象,它代表了了当前正在运行的协程,可以用它去获取协程的工作状态,可以通过它随时取消协程,
val job = GlobalScope.launch {
Log.e("Mike","start")
delay(2000)
Log.e("Mike","complete")
}
Thread.sleep(1000)
job.cancel()
Log.e("Mike","cancel")
打印结果
start
cancel
Job的生命周期
Job中提供了三个变量用来获取当先协程的执行情况
isActive
isCompleted
isCancelled
通过上述的三个变量可以很容易的判断协程当前是位于什么状态
New
: 当我们创建了协程,但是协程并没有启动时候,在之前我们介绍过协程的不同启动模式,当我们使用Lazy
模式去创建协程,协程并不会启动,而是会在New
状态
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {}
Log.e("Mike", "job states--isActive=${job.isActive} isCompleted=${job.isCompleted} isCancelled=${job.isCancelled} ")
打印结果
job states--isActive=false isCompleted=false isCancelled=false
Active
: 当协程启动之后,就会处于Active
状态,为了防止它进入下一状态所以增加了2秒的延时
val job = GlobalScope.launch() {
delay(2000)
}
Log.e("Mike", "job states--isActive=${job.isActive} isCompleted=${job.isCompleted} isCancelled=${job.isCancelled} ")
打印结果
job states--isActive=true isCompleted=false isCancelled=false
Completing
: 当协程已经执行完毕,但还需要等待它的子协程执行,此时它的状态是完成中状态,它和Active
所反映出的结果相同都是还没有执行完,差别在于此时主协程已经执行完毕,在等待子协程的执行
val job = GlobalScope.launch() {
launch {
delay(2000)
}
}
Log.e("Mike", "job states--isActive=${job.isActive} isCompleted=${job.isCompleted} isCancelled=${job.isCancelled} ")
打印结果
job states--isActive=true isCompleted=false isCancelled=false
Completed
: 当协程已经完全执行完毕,包括内部的子协程
val job = GlobalScope.launch() {
launch {
delay(2000)
}
}
Thread.sleep(3000)
Log.e("Mike", "job states--isActive=${job.isActive} isCompleted=${job.isCompleted} isCancelled=${job.isCancelled} ")
打印结果
job states--isActive=false isCompleted=true isCancelled=false
Cancelling Cancelled
: 当协程被取消或者有异常产生时,会进入此状态,如果父协程此时cancel
掉,则会停留在Cancelling
状态等待子协程·cancel
,当内部子协程结束完毕之后才会进入Cancelled
状态,当结束之后会回调invokeOnCompletion
函数
val job = GlobalScope.launch {
launch {
delay(2000)
}
}
job.invokeOnCompletion {
Log.e("Mike", "job states completed--isActive=${job.isActive} isCompleted=${job.isCompleted} isCancelled=${job.isCancelled} ")
}
job.cancel()
Log.e("Mike", "job states--isActive=${job.isActive} isCompleted=${job.isCompleted} isCancelled=${job.isCancelled} ")
打印结果
job states--isActive=false isCompleted=false isCancelled=true
//稍后
job states--isActive=false isCompleted=true isCancelled=true
取消一个协程
使用Scope
去cancel
使用此Scope
所创建的协程
val scope = object : CoroutineScope {
override val coroutineContext: CoroutineContext = Dispatchers.IO + Job()
}
val job = scope.launch {
Log.e("Mike", "main1 start")
delay(3000)
Log.e("Mike", "main2 end")
}
val job1 = scope.launch {
Log.e("Mike", "main2 start")
delay(3000)
Log.e("Mike", "main2 end")
}
Thread.sleep(1000)
scope.cancel()
打印结果
main1 start
main2 start
Scope
中的cancel
扩展函数其实也是调用了Job
中的cancel
函数,所以如果你的Scope
没有Job
,比如GlobalScope
和MainScope
,如果调用cancel则会抛出异常提示Scope cannot be cancelled because it does not have a job
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
job.cancel(cause)
}
使用Job
去cancel
掉对应的协程
使用Job
可以cancel
掉它所对应的协程,以及子协程,也可以直接取消子协程,被取消的子协程不会影响其他同级的协程
- 当父协程取消时,子协程也会跟随者取消
val job = GlobalScope.launch() {
val childJob = launch {
delay(2000)
}
childJob.invokeOnCompletion {
Log.e("Mike", "child job canceled")
}
delay(3000)
}
job.invokeOnCompletion {
Log.e("Mike", "parent job canceled")
}
Thread.sleep(1000)
job.cancel()
打印结果
child job canceled
parent job canceled
- 当子协程出现没有捕获的异常时,异常会传递给其他子协程造成取消,也会造成父协程的取消
val errorHandle = CoroutineExceptionHandler { context, error ->
Log.e("Mike", "coroutine error $error")
}
val job = GlobalScope.launch(errorHandle) {
val childJob = launch {
delay(1000)
throw Exception("exception")
}
childJob.invokeOnCompletion {
Log.e("Mike", "child job canceled")
}
val childJob1 = launch {
delay(5000)
}
childJob1.invokeOnCompletion {
Log.e("Mike", "child job1 canceled")
}
delay(3000)
}
job.invokeOnCompletion {
Log.e("Mike", "parent job canceled")
}
打印结果
child job1 canceled
child job canceled
coroutine error java.lang.Exception: exception
parent job canceled
Job中的常用函数/属性
cancel
用于Job的取消,取消协程
start
用于启动一个协程,让其到达Active状态
invokeOnCompletion
添加一个监听,当工作完成或者异常时会调用
join
阻塞并等候当前协程完成
children
子Job链
Job的结构
前面我们提到了取消父Job
也会引起子Job
的取消,这是因为Job
是一个链表结构,每一个Job
会持有父Job
以及子Job
的对象,这样的结构层级关系组成了Job
的结构,探讨下它是如何实现的
首先,Job是一个Element
,所以它是CoroutineContext
的组成部分
public interface Job : CoroutineContext.Element
默认启动模式下,当我们创建一个协程时,他会和newContext
生成一个Job,它是StandaloneCoroutine
类型,newContext
是结合传入的Context
对象以及当前作用域Context
对象结合生成的当前协程的Context
对象
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
}
StandaloneCoroutine继承自AbstractCoroutine
,将newContext
传入其构造函数
private open class StandaloneCoroutine(
parentContext: CoroutineContext,
active: Boolean
) : AbstractCoroutine(parentContext, active) {
override fun handleJobException(exception: Throwable): Boolean {
handleCoroutineException(context, exception)
return true
}
}
然后调用了coroutine.start(start, coroutine, block)
,也就是调用了AbstractCoroutine
的start
函数
/**
* Starts this coroutine with the given code [block] and [start] strategy.
* This function shall be invoked at most once on this coroutine.
*
* First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it
* during construction. Second, it starts the coroutine based on [start] parameter:
*/
public fun start(start: CoroutineStart, block: suspend () -> T) {
initParentJob()
start(block, this)
}
在initParentJob
中会将当前Job对象添加到父Job
中通过attachChild
,如果父Job
存在,并且在这之前如果父协程没有启动则会将其启动
internal fun initParentJob() {
initParentJobInternal(parentContext[Job])
}
internal fun initParentJobInternal(parent: Job?) {
check(parentHandle == null)
if (parent == null) {
parentHandle = NonDisposableHandle
return
}
parent.start() // make sure the parent is started
@Suppress("DEPRECATION")
val handle = parent.attachChild(this)
parentHandle = handle
// now check our state _after_ registering (see tryFinalizeSimpleState order of actions)
if (isCompleted) {
handle.dispose()
parentHandle = NonDisposableHandle // release it just in case, to aid GC
}
}
我们再去看下attachChild
做了什么,AbstractCoroutine
继承了JobSupport
,attachChild
实现在里面
public final override fun attachChild(child: ChildJob): ChildHandle {
return invokeOnCompletion(onCancelling = true, handler = ChildHandleNode(this, child).asHandler) as ChildHandle
}
将this(parent Job),child Job
封装成了ChildHandleNode
传入了invokeOnCompletion
,此时ChildHandler
的结构为{childJob:子Job对象,job:父Job对象}
public final override fun invokeOnCompletion(
onCancelling: Boolean,
invokeImmediately: Boolean,
handler: CompletionHandler
): DisposableHandle {
var nodeCache: JobNode<*>? = null
loopOnState { state ->
when (state) {
is Empty -> { // EMPTY_X state -- no completion handlers
if (state.isActive) {
// try move to SINGLE state
val node = nodeCache ?: makeNode(handler, onCancelling).also { nodeCache = it }
if (_state.compareAndSet(state, node)) return node
} else
promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
}
is Incomplete -> {
val list = state.list
if (list == null) { // SINGLE/SINGLE+
promoteSingleToNodeList(state as JobNode<*>)
} else {
var rootCause: Throwable? = null
var handle: DisposableHandle = NonDisposableHandle
if (onCancelling && state is Finishing) {
synchronized(state) {
// check if we are installing cancellation handler on job that is being cancelled
rootCause = state.rootCause // != null if cancelling job
// We add node to the list in two cases --- either the job is not being cancelled
// or we are adding a child to a coroutine that is not completing yet
if (rootCause == null || handler.isHandlerOf() && !state.isCompleting) {
// Note: add node the list while holding lock on state (make sure it cannot change)
val node = nodeCache ?: makeNode(handler, onCancelling).also { nodeCache = it }
if (!addLastAtomic(state, list, node)) return@loopOnState // retry
// just return node if we don't have to invoke handler (not cancelling yet)
if (rootCause == null) return node
// otherwise handler is invoked immediately out of the synchronized section & handle returned
handle = node
}
}
}
if (rootCause != null) {
// Note: attachChild uses invokeImmediately, so it gets invoked when adding to cancelled job
if (invokeImmediately) handler.invokeIt(rootCause)
return handle
} else {
val node = nodeCache ?: makeNode(handler, onCancelling).also { nodeCache = it }
if (addLastAtomic(state, list, node)) return node
}
}
}
else -> { // is complete
// :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
// because we play type tricks on Kotlin/JS and handler is not necessarily a function there
if (invokeImmediately) handler.invokeIt((state as? CompletedExceptionally)?.cause)
return NonDisposableHandle
}
}
}
}
invokeOnCompletion
看起来很复杂
loopOnState
是一个死循环,循环读取Internal states
到代码块中
当我们添加Child
的时候会进入Empty
中的代码,并且此时nodeCache
为null
is Empty -> { // EMPTY_X state -- no completion handlers
if (state.isActive) {
// try move to SINGLE state
val node = nodeCache ?: makeNode(handler, onCancelling).also { nodeCache = it }
if (_state.compareAndSet(state, node)) return node
} else
promoteEmptyToNodeList(state) // that way we can add listener for non-active coroutine
}
然后会执行makeNode
,此时的执行上下文为
this
: 表示parent Job
handler
: 是一个ChildHandleNode
封装了Child
的Job
val node = nodeCache ?: makeNode(handler, onCancelling).also { nodeCache = it }
然后进入到了makeNode
的if
,因为onCancelling == true
private fun makeNode(handler: CompletionHandler, onCancelling: Boolean): JobNode<*> {
return if (onCancelling)
(handler as? JobCancellingNode<*>)?.also { require(it.job === this) }
?: InvokeOnCancelling(this, handler)
else
(handler as? JobNode<*>)?.also { require(it.job === this && it !is JobCancellingNode) }
?: InvokeOnCompletion(this, handler)
}
handler as? JobCancellingNode<*>
条件满足,因为ChildHandlerNode
间接继承自JobCancellingNode
if (_state.compareAndSet(state, node)) return node
然后会返回ChildHandlerNode
对象 return
,并将parent
的state
设置为node
回到initParentJobInternal
中,由当前子Job
的parentHandle
字段持有
val handle = parent.attachChild(this)
parentHandle = handle
当取消时会调用notifyCancelling
,进入cancelling
状态先取消自己的child
,然后再取消parent
private fun notifyCancelling(list: NodeList, cause: Throwable) {
// first cancel our own children
onCancelling(cause)
notifyHandlers>(list, cause)
// then cancel parent
cancelParent(cause) // tentative cancellation -- does not matter if there is no parent
}
在cancelParent
中调用了之前存入parentHandle
,将父Job
取消掉
private fun cancelParent(cause: Throwable): Boolean {
// CancellationException is considered "normal" and parent is not cancelled when child produces it.
// This allow parent to cancel its children (normally) without being cancelled itself, unless
// child crashes and produce some other exception during its completion.
if (cause is CancellationException) return true
if (!cancelsParent) return false
return parentHandle?.childCancelled(cause) == true
}
将父Job
对象放入到了子ChildHandlerNode
对象的job
字段中去,并且父Job也持有了子Job的对象,这个是一种链表的实现,这个结构对我们来讲很重要,也解释了Job
之间的关联关系,父子Job之间的链表结构,正是实现结构化并发的条件
欢迎关注Mike的
Android知识整理