Introduce Coroutine

James Lin
6 min readJan 18, 2020

--

本篇將會介紹
1. CoroutineScope
2. Dispatchers
3. Job
4. Handle Exception
5. Exception Path

1. CoroutineScope

CoroutineScope 就是 Job 的執行範圍,舉個例子:
今天你打算刷完某個副本(遊戲),但是你使用電腦的時間是 19:00~22:00,時間到的時候,你發現還沒打完,這時只能取消這個念頭,關掉電腦.
Job:刷完某個副本
Scope:19:00~22:00
當 Job 執行的時間超出 Scope 時,將會被強制取消,這就是 CoroutineScope 的作用.

2. Dispatchers

負責把 Job 分派給執行緒,舉個例子:
今天公司的主管將 A 任務分給 A 員工, B 任務分給 B 員工.
Dispatchers : 主管
Job : 任務

3. Job

在 Coroutine 的世界裡,Job 就是任務,注意一點 Job 有 parent-child 的關係.舉個例子:

lifecycleScope.launch {
witchContext(Dispatchers.IO) {
.....
}
}

在上述的程式碼中 launch 就是一個 Job,而 withContext 就是 launch 的 Child Job.

4. Handle Exception

Coroutine 處理 Exception 的機制非常依賴 Job,舉個例子:

val exceptionHandler = CoroutineExceptionHandler { 
coroutineContext, throwable ->
Log.d("exp"," exceptionHandler")
}

lifecycleScope.launch(exceptionHandler) {
val defferResult = async {
throw Exception()
}
try {
val result = defferResult.await()
} catch (e: Exception) {
Log.d("exp"," try-catch")
}
}

asynclaunch 的 Child Job,當發生 Exception 的時候,Child Job 會將 Exception 傳遞給 Parent Job,導致 execeptionHandler 與 try-catch 都會有接到 Exception.

5. Exception Path

發生 Exception 時 Job 會將 Exception 傳遞給 Parent Job,那這個 Path 有幾個規則:
1. Child Job 會詢問 Parent Job :你是否可以處理這個 Exception
2. Parent Job 接收到 Exception 後會回應 Child Job:”是,我可以處理這個 Exception” 或者 “不,這個 Exception 你必須自己處理”.
3. 處理的方式會依據 Job 的不同而有不同的 Path

介紹 Job 對於 Exception 的處理方式:

  1. launch :會將 Exception 傳遞給 Parent Job,如果 Parent Job 無法處理 Exception,launch將把 Exception 交給 ExceptionHandler 處理,如果沒有 ExceptionHandler 那麼這個 Exception 將會拋給 thread.uncaughtexceptionhandler 處理.
  2. async: 會將 Exception 傳遞給 Parent Job,如果 Parent Job 無法處理 Exception,async什麼事都不會做;但是 “呼叫 await“ 時會將 Exception 重新拋出.
  3. withContext :接受 Child Job 的 Exception,不會將 Exception 傳遞給 Parent Job,但會將 Exception 重新拋出.
  4. coroutineScope :接受 Child Job 的 Exception,不會將 Exception 傳遞給 Parent Job,但會將 Exception 重新拋出.
  5. SupervisorJob, supervisorScope:不接受 Child Job 的 Exception,強制 Child Job 要自己處理 Exception;不會將 Exception 傳遞給 Parent Job,但會將 Exception 重新拋出.
  6. Job() : 如果有 Parent Job 則 接受 Child Job 的 Exception 並交由 Parent Job 處理,如果沒有則 拒絕 Child Job 的 Exception,強制 Child Job 自行處理 Exception.

針對 withContext, coroutineScope, SupervisorJob, supervisorScope 做個比較

withContext, coroutineScope:由於 withContextcoroutineScope 會跟 launch 說 “我會處理 Exception”,接著會將 Exception 重新拋出,所以外面的 try-catch 才接得到 Exception.

lifecycleScope.launch {
try {
withContext {
launch {
throw Exception()
}
}
} catch (e: Exception){
Log.d("exp", " receive")
}
}

SupervisorJob:由於 launch 有設定 Parent Job 是 SupervisorJob,所以 SupervisorJob 會跟 launch 說 “我無法處理 Exception”,進而觸發 launch 的 Exception Handle 機制將 Exception 交給 exceptionHandler 處理.

val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->  
Log.d("exp"," exceptionHandler")
}
lifecycleScope.launch {
launch(SupervisorJob() + exceptionHandler) {
throw Exception()
}
}

SupervisorScope:SupervisorScope 會跟launch 說“我無法處理 Exception”,進而觸發 launch 的 Exception Handle 機制將 Exception 交給 exceptionHandler 處理.

val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->  
Log.d("exp"," exceptionHandler")
}
lifecycleScope.launch {
supervisorScope {
launch(exceptionHandler) {
throw Exception()
}
}
}

--

--

James Lin
James Lin

No responses yet