Job
中文為工作、任務。
import
1
2
3
4
5
6
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Job
import kotlinx.coroutines.job
import kotlinx.coroutines.runBlocking
import org.junit.Test
Job 生命周期
Job的狀態有New(建立)、Active、Completing(正要完成中)、Completed(已完成)、Cancelling(取消中)、Cancelled(已取消)。
Job生命周期圖
- New 建立: Job建立。
- Active 運行中: Job.start()啟動。
- child子協程加入job並啟動。
- Completing 完成中: child.join()等待子協程完成。
- Completed 已完成: Job與child子協程都執行完畢。

生命周期屬性是:isActive(是否執行中)、isCancelled(是否取消)、isCompleted(是否完成)。
建立空的Job
1
val job = Job()
啟動Job
1
2
3
4
5
6
7
8
@Test
fun coroutin10() = runBlocking {
// New
val job = Job()
// Start
job.start()
println("job = ${job.isActive}")
}
job = true
建立啟動子協程
launch()是建立並啟動新協程,launch()參數是父親job,代表這個新協程是job的子協程。
協程是通過launch協程構建器創建的,它運行指定的代碼塊,並在該代碼塊完成時結束。
launch(job) { 要執行的程式 }
launch會自動啟動start()。
1
2
3
val child = launch(job) {
delay(1000)
}
launch程式碼,會自動呼叫start()
1
2
3
4
5
6
7
8
9
10
11
12
13
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)
// launch會呼叫start()
coroutine.start(start, coroutine, block)
return coroutine
}
runBlocking 等待協程執行完畢
join()代表等待,runBlocking{}是一個協程,裡面有一個協程child在執行,注意child的爸爸是Job,不是runBlocking,runBlocking與Job二者是不相同,runBlocking與child沒有父子關係,純粹就是等待協程執行完畢。

fun coroutin10() = runBlocking {
.
.
.
// 等待協程執行完畢
child.join()
// 子協程已執行完畢
.
.
.
}
子協程完成狀態
在join()之前,子協程仍在執行中,join()之後,子協程執行完畢。
但即便子協程執行完畢,Job是父親,isActive = true 狀態仍在執行中,並未完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
fun coroutin10() = runBlocking {
// New 建立
val job = Job()
// Start 啟動
job.start()
println("job isActive= ${job.isActive}")
// job加入子協程,並啟動子協程
val child = launch(job) {
delay(1000)
}
// 子協程執行中
println("child isActive= ${child.isActive}")
// 等待子協程完成
// wait children complete
child.join()
// 子協程已完成
// children completed
println("child isActive= ${child.isActive}")
println("child isCompleted= ${child.isCompleted}")
// job仍未完成,仍在完成中...
// job Completing
println("job isActive= ${job.isActive}")
}
job isActive= true
child isActive= true
child isActive= false
child isCompleted= true
job isActive= true
手動完成
job.complete()可以把Job狀態變成已完成。
1
2
3
4
5
if (job is CompletableJob) {
job.complete()
println("After complete job isActive = ${job.isActive}")
println("After complete job isCompleted= ${job.isCompleted}")
}
完整程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
fun coroutin10() = runBlocking {
// New
val job = Job()
// Start
job.start()
println("job isActive= ${job.isActive}")
val child = launch(job) {
delay(1000)
}
println("child isActive= ${child.isActive}")
// wait children complete
child.join()
// children completed
println("child isActive= ${child.isActive}")
println("child isCompleted= ${child.isCompleted}")
// job Completing
println("job isActive= ${job.isActive}")
if (job is CompletableJob) {
job.complete()
println("After complete job isActive = ${job.isActive}")
println("After complete job isCompleted= ${job.isCompleted}")
}
}
job isActive= true
child isActive= true
child isActive= false
child isCompleted= true
job isActive= true
After complete job isActive = false
After complete job isCompleted= true
Job父子關係
coroutineContext
以下二種方法都是取得目前的Job物件,Job物件為launch{}的傳回值。
1
2
coroutineContext.job
coroutineContext[Job]
若試圖輸出coroutineContext.job,會有以下格式。
Job名字 #編號: 協程作用域 {Job狀態} @ Job物件hashCode
coroutine#1":BlockingCoroutine{Active}@7674b62c
| 每一個區塊 | 含義 |
| coroutine#1 | 協程的內部編號(第一個被建立的 coroutine) |
| BlockingCoroutine | 協程的類型(這裡是由 runBlocking 建立的 coroutine) |
| {Active} | 協程目前的狀態:可能是 Active、Completed、Cancelled 等 |
| @7674b62c | 物件的 hash code(十六進位表示) |
下圖中,綠色的Job物件為runBlocking{}的傳回值為BlockingCoroutine。
紅色的Job物件為launch{}的傳回值child。

執行結果可以知道job的父親是null,child的父親是job。

而runBlocking傳回的是BlockingCoroutine。
child的祖父不是BlockingCoroutine,都是分開來。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun coroutin11() = runBlocking {
println("runBlocking = ${coroutineContext[Job]}")
println("runBlocking = ${coroutineContext.job}")
val job = Job()
job.start()
println("job parent = ${job.parent}")
println("job = $job")
val child = launch(job) {
delay(1000)
println("inner child = ${coroutineContext[Job]}")
println("inner child = ${coroutineContext.job}")
}
println("child parent= ${child.parent}")
println("child = ${child}")
child.join()
}
runBlocking = "coroutine#1":BlockingCoroutine{Active}@7674b62c
runBlocking = "coroutine#1":BlockingCoroutine{Active}@7674b62c
job parent = null
job = JobImpl{Active}@5e4bd84a
child parent= JobImpl{Active}@5e4bd84a
child = "coroutine#3":StandaloneCoroutine{Active}@2a62b5bc
inner child = "coroutine#3":StandaloneCoroutine{Active}@2a62b5bc
inner child = "coroutine#3":StandaloneCoroutine{Active}@2a62b5bc
isActive isCompleted isCancelled
coroutineContext.job有isActive、isCompleted、isCancelled屬性。
1
2
3
println("isActive: ${coroutineContext.job.isActive}")
println("isCompleted: ${coroutineContext.job.isCompleted}")
println("isCancelled: ${coroutineContext.job.isCancelled}")
Job中的協程數量
取得Job中尚未完成子協程數量。
Job名.children.count()
childJob的父親是parent,childJob下面有2個子協程,分別是child1與child2。
child2會先執行完畢,因為它才delay2秒,此時的childJob.children.count() = 1,代表剩下一個子協程未完成。
等到delay 3秒後,child1與child2全執行完畢,childJob的狀態也變成已完成。isCompleted = true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fun coroutin09() = runBlocking {
val parent = Job()
val childJob = launch(parent) {
val child1 = launch {
delay(3000)
println("child1 finish")
}
val child2 = async {
delay(2000)
println("child2 finish")
}
child1.start()
child2.start()
}
// 2.1秒後
delay(2100)
println("childJob isActive = ${childJob.isActive}")
println("childJob 尚未完成數量 = ${childJob.children.count()}")
// 2.1秒+1秒 = 3.1秒後
delay(1000)
println("childJob 尚未完成數量 = ${childJob.children.count()}")
println("childJob isActive = ${childJob.isActive}")
println("childJob isCompleted = ${childJob.isCompleted}")
}
child2 finish
childJob isActive = true
childJob 尚未完成數量 = 1
child1 finish
childJob 尚未完成數量 = 0
childJob isActive = false
childJob isCompleted = true
job啟動所有子協程
runBlocking對listof的物件,不會自動start()。
本例使用runBlocking。
job是所有list中的子協程的父親,job.start(),所有子協程全啟動。
使用it.join()讓runBlocking協程等待所有協程執行完畢。
注意!runBlocking不是list中子協程的父親,runBlocking只負責「等待」別的協程執行完畢。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun coroutin13() = runBlocking {
val job = Job()
val list = listOf(
launch(job) {
delay(1000)
println("list[0] finish")
},
launch(job) {
delay(2000)
println("list[1] finish")
})
job.start()
list.forEach { it.join() }
println("all children finish.")
}
list[0] finish
list[1] finish
all children finish.
未指派父親Job
等號右邊 = runBlocking,launch{}沒給任何job父親參數,預設runBlocking為job1的父協程。

因為runBlocking是父協程,父協程會自動等待子協程job1執行完畢,不用加上job1.join(),因為二者是父子關係。
1
2
3
4
5
6
fun coroutin01() = runBlocking {
val job1 = launch {
delay(200)
println("job1 finished")
}
}
job1 finished
this
如果只用this會混淆這個this物件是誰,可以使用@標籤名,來區別這個this是誰。
可以使用以下方法,印出物件的類別。
this@launch::class.simpleName
1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
fun coroutin12() = runBlocking {
println("outside this = $this")
val job = Job()
val child = launch(job) {
println("inner this = $this")
println("launch this = ${this@launch}")
println("runBlocking this = ${this@runBlocking}")
println("launch class = ${this@launch::class.simpleName}")
println("runBlocking class = ${this@runBlocking::class.simpleName}")
}
child.join()
}
outside this = "coroutine#1":BlockingCoroutine{Active}@662706a7
inner this = "coroutine#2":StandaloneCoroutine{Active}@488d1cd7
launch this = "coroutine#2":StandaloneCoroutine{Active}@488d1cd7
runBlocking this = "coroutine#1":BlockingCoroutine{Active}@662706a7
launch class = StandaloneCoroutine
runBlocking class = BlockingCoroutine