Job是标准库中启动协程后返回的对象,代表着协程本次作业。我们可以判断协程是否结束,是否取消,是否完成并且可以取消当前协程以及嵌套子协程。
一个job 可以理解成他就是一个协程对象。这个job可以开始、取消、状态就监听等操作。
我们简单的看下面的代码如何获取job对象
fun main() {
val job = GlobalScope.launch {
}
//协程是否存活
job.isActive
//是否取消
job.isCancelled
//是否完成
job.isCompleted
//取消协程
job.cancel()
//(非懒启动模式的)启动协程
job.start()
}
Job继承关系
首先我们注意一点:他继承Element证明他能被放入协程上下文中,且Key为Job。在标准库启动协程后Job对象将放入其上下文中
我们看先一个简单的示例代码:
fun log(msg: String) {
println("[${Thread.currentThread().name}] ${msg}")
}
fun main() {
val job = GlobalScope.launch {
//获取当前的上下文
val job = coroutineContext
log("协程内部job:$job")
val job2=launch {
val job2=coroutineContext
log("协程内部job2:$job2")
}
log("协程内部job2:$job2")
}
//协程返回的job和内部或者是同一对象
log("协程外部的job:$job")
TimeUnit.HOURS.sleep(1)
}
[main] 协程外部的job:StandaloneCoroutine{Active}@73c6c3b2
[DefaultDispatcher-worker-1] 协程内部job:[StandaloneCoroutine{Active}@73c6c3b2, DefaultDispatcher]
[DefaultDispatcher-worker-1] 协程内部job2:StandaloneCoroutine{Active}@749f0c36
[DefaultDispatcher-worker-2] 协程内部job2:[StandaloneCoroutine{Active}@749f0c36, DefaultDispatcher]
Job 状态
Job有三个暴露的函数用于判断其状态:
- isActive 当前协程是否存活(创建协程并没有启动协程的时候返回false)
- isCompleted 当前协程是否完成
- isCancelle 当前协程是否取消
Demo说明:
fun main() {
//启动一个懒启动 CoroutineStart.LAZY用说明isActive不一定
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
//获取当前的上下文
TimeUnit.MILLISECONDS.sleep(500)
log("协程完成")
}
log("未启动协程之前的状态: isActive: [${job.isActive}] isCompleted: [${job.isCompleted}] isCancelled: [${job.isCancelled}] ")
job.start()
log("启动协程之后的状态: isActive: [${job.isActive}] isCompleted: [${job.isCompleted}] isCancelled: [${job.isCancelled}] ")
TimeUnit.MILLISECONDS.sleep(600)
log("启动协程完成之后的状态: isActive: [${job.isActive}] isCompleted: [${job.isCompleted}] isCancelled: [${job.isCancelled}] ")
TimeUnit.HOURS.sleep(1)
}
[main] 未启动协程之前的状态: isActive: [false] isCompleted: [false] isCancelled: [false]
[main] 启动协程之后的状态: isActive: [true] isCompleted: [false] isCancelled: [false]
[DefaultDispatcher-worker-1] 协程完成
[main] 启动协程完成之后的状态: isActive: [true] isCompleted: [false] isCancelled: [false]
取消状态Demo:
suspend fun main() {
//启动一个懒启动 CoroutineStart.LAZY用说明isActive不一定
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
//获取当前的上下文
TimeUnit.MILLISECONDS.sleep(500)
log("协程完成")
}
log("未启动协程之前的状态: isActive: [${job.isActive}] isCompleted: [${job.isCompleted}] isCancelled: [${job.isCancelled}] ")
job.start()
log("启动协程之后的状态: isActive: [${job.isActive}] isCompleted: [${job.isCompleted}] isCancelled: [${job.isCancelled}] ")
job.cancel()
log("启动协程取消之后的状态: isActive: [${job.isActive}] isCompleted: [${job.isCompleted}] isCancelled: [${job.isCancelled}] ")
job.join()
log("启动协程取消并且之后的状态: isActive: [${job.isActive}] isCompleted: [${job.isCompleted}] isCancelled: [${job.isCancelled}] ")
TimeUnit.HOURS.sleep(1)
}
[main] 未启动协程之前的状态: isActive: [false] isCompleted: [false] isCancelled: [false]
[main] 启动协程之后的状态: isActive: [true] isCompleted: [false] isCancelled: [false]
[main] 启动协程取消之后的状态: isActive: [false] isCompleted: [false] isCancelled: [true]
[DefaultDispatcher-worker-1] 协程完成
[DefaultDispatcher-worker-1] 启动协程取消并且之后的状态: isActive: [false] isCompleted: [true] isCancelled: [true]
当然Job有自己内部的状态变化,然后这些内部的变化比较多,然后部分变化也就对应了我们isActive,isCompleted,isCancelled三个状态的数值。
简要的说明下内部的状态变化:
New
当我们创建一个协程并未启动时候就是出于New
状态(使用CoroutineStart.LAZY
方式时便可看到).
Active
当我们调用job.start
函数的之后,且当前job
没有执行完成
Cancelling
当我们调用取消的时候立即进入此状态
Cancelled
最终取消任务完成
Completing
当协程完成的时候需要等候子协程完成的状态
Completed
协程完成
Job 一些常用方法或者属性
-
isActive
协程是否存活(注意懒启动) -
isCancelled
协程是否取消 -
isCompleted
协程是否完成 -
cancel()
取消协程 -
start()
启动协程 -
join()
阻塞等候协程完成 -
cancelAndJoin()
取消并等候协程完成 -
invokeOnCompletion( onCancelling: Boolean = false, invokeImmediately: Boolean = true, handler: CompletionHandler)
监听协程的状态回调 -
attachChild(child: ChildJob)
附加一个子协程到当前协程上
单独说下 invokeOnCompletion
函数:
invokeOnCompletion
函数用于监听其完成或者其取消状态,
参数一:onCancelling
参数用于判断是否监听取消事件否则监听完成事件,
参数二:invokeImmediately
参数主要用于监听已经完成协程时是否回调,如果为false 那么如果在添加监听事件时协程已经完成或取消那么将不会回调.
fun main() = runBlocking{
val job = GlobalScope.launch {
}
job.join()
job.invokeOnCompletion(onCancelling = false, invokeImmediately = false) {
log("取消的回调"+it)
}
TimeUnit.SECONDS.sleep(10000)
log("main 结束")
}
上面的例子中:
log("取消的回调"+it)
永不执行