协程 了解Job的生命周期和用法
前言
在前面的文章中,咱们学习了协程的基本概念,以及怎么运用launch和async函数来创立和发动协程。可是,创立和发动协程并不是协程的悉数,咱们还需求知道怎么监测和操控协程的状况和履行流程。这便是本文要介绍的内容,即协程的Job的概念,以及怎么经过Job来管理协程。
Job和协程的关系
首要,咱们来经过一个比方,来说明Job和协程的关系。假定你要去游览,你需求订一张机票。你能够经过网上订票体系来完结这个使命。当你提交了你的信息和付出办法后,网上订票体系会给你回来一个订单号,这个订单号就相当于一个Job。经过这个订单号,你能够查询你的机票状况,比方是否现已出票,是否能够改签或退票等。假如你改变了主见或许有其他原因,你也能够经过这个订单号来撤销你的机票。
同样地,当咱们创立一个协程时,咱们也会得到一个Job方针。这个Job方针就代表了这个协程的使命。经过这个Job方针,咱们能够查询协程的状况,比方是否正在运转,是否现已撤销或完结等。咱们也能够经过这个Job方针来撤销协程,假如咱们不想让它继续履行或许有其他原因。
Job的作用
那么,Job到底是什么呢?官方文档给出了这样的界说:
A background job. Conceptually, a job is a cancellable thing with a life-cycle that culminates in its completion.
翻译过来便是:
一个后台使命。从概念上讲,一个使命是一个可撤销的东西,它有一个以其完结为终点的生命周期。
从这个界说中,咱们能够看出Job能够做两件事情:
- 监测协程的状况
- 操控协程的履行
下面咱们分别来看看这两件事情。
Job的状况
Job有三个外部状况特点,分别是:
- isActive: 表明协程是否正在运转
- isCancelled: 表明协程是否现已被撤销
- isCompleted: 表明协程是否现已完结
留意:这三个特点并不是互斥的,它们或许同时为true或false。比方,在协程被撤销后,它或许还在运转一段时间(直到遇到撤销查看点),此刻isActive和isCancelled都为true;在协程完结后,它或许是正常完结也或许是反常完结(被撤销或抛出反常),此刻isCompleted和isCancelled都为true。
状况 | isActive | isCancelled | isCompleted |
---|---|---|---|
New | false | false | false |
Active | true | false | false |
Completing | true | false | false |
Cancelling | false | true | false |
Cancelled | false | true | true |
Completed | false | false | true |
咱们能够经过这三个特点来判断协程的状况,并依据不同的状况做出相应的处理。比方:
// 创立一个协程
val job = GlobalScope.launch {
// 模仿一个耗时操作
delay(1000)
println("Hello, world!")
}
// 在主线程中查看协程的状况
println("isActive: ${job.isActive}") // true
println("isCancelled: ${job.isCancelled}") // false
println("isCompleted: ${job.isCompleted}") // false
// 等候一段时间
Thread.sleep(1500)
// 再次查看协程的状况
println("isActive: ${job.isActive}") // false
println("isCancelled: ${job.isCancelled}") // false
println("isCompleted: ${job.isCompleted}") // true
输出成果:
isActive: true
isCancelled: false
isCompleted: false
Hello, world!
isActive: false
isCancelled: false
isCompleted: true
Job的办法
Job有两个办法,分别是:
- start(): 发动协程
- cancel(): 撤销协程
这两个办法能够影响协程的状况,然后影响协程的履行流程。咱们来看看它们的用法和效果。
start()
start()办法用于发动协程。通常情况下,咱们不需求显式地调用这个办法,由于当咱们运用launch或async函数创立协程时,它们会自动调用这个办法。可是,假如咱们想要推迟发动协程,或许手动操控协程的发动机遇,咱们能够运用CoroutineStart.LAZY参数来创立协程,并在需求的时分调用start()办法。比方:
// 创立一个推迟发动的协程
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
// 模仿一个耗时操作
delay(1000)
println("Hello, world!")
}
// 在主线程中查看协程的状况
println("isActive: ${job.isActive}") // false
println("isCancelled: ${job.isCancelled}") // false
println("isCompleted: ${job.isCompleted}") // false
// 发动协程
job.start()
// 再次查看协程的状况
println("isActive: ${job.isActive}") // true
println("isCancelled: ${job.isCancelled}") // false
println("isCompleted: ${job.isCompleted}") // false
// 等候一段时间
Thread.sleep(1500)
// 再次查看协程的状况
println("isActive: ${job.isActive}") // false
println("isCancelled: ${job.isCancelled}") // false
println("isCompleted: ${job.isCompleted}") // true
输出成果:
isActive: false
isCancelled: false
isCompleted: false
isActive: true
isCancelled: false
isCompleted: false
Hello, world!
isActive: false
isCancelled: false
isCompleted: true
cancel()
cancel()办法用于撤销协程。当咱们调用这个办法时,它会将协程的状况标记为撤销状况,并测验停止协程的履行。可是,这并不意味着协程会立即停止,由于协程或许还在运转一些不行撤销的代码,或许没有遇到撤销查看点。撤销查看点是指一些能够呼应撤销恳求的函数或句子,比方delay(), yield(), withContext(), withTimeout()等。只有当协程遇到了撤销查看点,它才会真实地停止履行,并抛出一个CancellationException反常。
咱们能够经过cancel()办法来终止一个不想要或许犯错的协程。比方:
// 创立一个协程
val job = GlobalScope.launch {
// 模仿一个耗时操作
delay(1000)
println("Hello, world!")
}
// 在主线程中查看协程的状况
println("isActive: ${job.isActive}") // true
println("isCancelled: ${job.isCancelled}") // false
println("isCompleted: ${job.isCompleted}") // false
// 撤销协程
job.cancel()
// 再次查看协程的状况
println("isActive: ${job.isActive}") // false
println("isCancelled: ${job.isCancelled}") // true
println("isCompleted: ${job.isCompleted}") // false
// 等候一段时间
Thread.sleep(1500)
// 再次查看协程的状况
println("isActive: ${job.isActive}") // false
println("isCancelled: ${job.isCancelled}") // true
println("isCompleted: ${job.isCompleted}") // true
输出成果:
isActive: true
isCancelled: false
isCompleted: false
isActive: false
isCancelled: true
isCompleted: false
Exception in thread "DefaultDispatcher-worker-1" kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@6d311334
isActive: false
isCancelled: true
isCompleted: true
Job的内部机制
Job有一个内部状况机制,用于表明和维护协程的生命周期。
Job的内部状况机制有以下几种状况:
- New: 协程刚刚被创立,还没有发动。
- Active: 协程现已发动,正在运转。
- Completing: 协程现已完结了它的主体逻辑,可是还有一些子协程没有完结。
- Cancelling: 协程现已被撤销,可是还有一些子协程或许不行撤销的代码没有完结。
- Cancelled: 协程现已被撤销,而且没有任何子协程或许不行撤销的代码在运转。
- Completed: 协程现已正常完结,而且没有任何子协程在运转。
这些状况之间的转化条件和含义如下:
- New -> Active: 当协程被发动时,它会从New状况转化为Active状况。这个转化能够由start()办法或许CoroutineStart.LAZY参数触发。
- Active -> Completing: 当协程完结了它的主体逻辑时,它会从Active状况转化为Completing状况。这个转化能够由return句子或许反常抛出触发。
- Active -> Cancelling: 当协程被撤销时,它会从Active状况转化为Cancelling状况。这个转化能够由cancel()办法或许超时函数触发。
- Completing -> Completed: 当协程没有任何子协程在运转时,它会从Completing状况转化为Completed状况。这个转化表明协程正常完结了它的使命。
- Cancelling -> Cancelled: 当协程没有任何子协程或许不行撤销的代码在运转时,它会从Cancelling状况转化为Cancelled状况。这个转化表明协程反常完结了它的使命,而且抛出了一个CancellationException反常。
- Cancelling -> Completed: 当协程被撤销时,假如它没有遇到任何撤销查看点,而且正常完结了它的使命,它会从Cancelling状况转化为Completed状况。这个转化表明协程虽然被撤销了,可是仍然履行了它的逻辑,而且回来了一个成果。
咱们能够经过下面的示意图来理解这些状况之间的转化:
等候和监听协程
除了查询和操控协程的状况外,咱们还能够经过两个办法来等候或监听协程的完结事情,以便在协程完毕后做一些后续处理。这两个办法分别是:
- join(): 等候协程完结
- invokeOnCompletion(): 监听协程完结
join()
join()办法用于等候协程完结。当咱们调用这个办法时,当时线程会被阻塞,直到方针协程完毕。咱们能够经过这个办法来同步协程和线程之间的履行流程。比方:
// 创立一个协程
val job = GlobalScope.launch {
// 模仿一个耗时操作
delay(1000)
println("Hello, world!")
}
// 在主线程中等候协程完结
println("Before join")
job.join()
println("After join")
输出成果:
Before join
Hello, world!
After join
invokeOnCompletion()
invokeOnCompletion()办法用于监听协程完结。当咱们调用这个办法时,咱们需求传入一个回调函数,这个回调函数会在方针协程完毕时被调用。咱们能够经过这个办法来异步地处理协程的完结事情,比方打印日志,释放资源,处理反常等。比方:
// 创立一个协程
val job = GlobalScope.launch {
// 模仿一个耗时操作
delay(1000)
println("Hello, world!")
}
// 在主线程中监听协程完结
job.invokeOnCompletion {
println("Job is done")
}
输出成果:
Hello, world!
Job is done
Deferred接口
Deferred接口是Job的子接口,它供给了一个额外的功用,便是等候协程回来成果。当咱们运用async函数创立协程时,咱们会得到一个Deferred方针,这个方针有一个await()函数,用于等候协程的成果。咱们能够经过这个函数来获取协程的回来值,并依据不同的值做出相应的处理。比方:
// 创立一个回来成果的协程
val deferred = GlobalScope.async {
// 模仿一个耗时操作
delay(1000)
// 回来一个随机数
(1..10).random()
}
// 在主线程中等候协程的成果
val result = deferred.await()
println("Result is $result")
输出成果:
Result is 7
总结
本文介绍了协程的Job的概念,以及怎么经过Job监测和操控协程的状况和履行流程。咱们学习了Job的三个状况特点,两个办法,以及它的内部状况机制。咱们还学习了怎么等候或监听协程的完结事情,以及怎么获取协程的回来成果。经过这些知识,咱们能够更好地管理和操控协程的生命周期,然后提高协程的可靠性和效率。
重视大众号:Android老皮!!!欢迎大家来找我探讨沟通