协程 了解Job的生命周期和用法

前言

在前面的文章中,咱们学习了协程的基本概念,以及怎么运用launch和async函数来创立和发动协程。可是,创立和发动协程并不是协程的悉数,咱们还需求知道怎么监测和操控协程的状况和履行流程。这便是本文要介绍的内容,即协程的Job的概念,以及怎么经过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状况。这个转化表明协程虽然被撤销了,可是仍然履行了它的逻辑,而且回来了一个成果。

咱们能够经过下面的示意图来理解这些状况之间的转化:

协程 了解Job的生命周期和用法

等候和监听协程

除了查询和操控协程的状况外,咱们还能够经过两个办法来等候或监听协程的完结事情,以便在协程完毕后做一些后续处理。这两个办法分别是:

  • 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

协程 了解Job的生命周期和用法

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老皮!!!欢迎大家来找我探讨沟通