Android Coroutine - Supervision
Supervision
Coroutine에서 Exception이 발생한 경우 Exception이 전파되면서 다른 자식들의 Coroutine을 취소시키고, 부모까지 취소시킨다. 하지만 여러 Coroutine에서 API를 호출하여 UI를 업데이트 하는 작업을 생각해보자. 하나의 API가 실패한 경우 모든 Coroutine이 취소되면서 다른 작업까지 영향을 미친다. 이러한 문제점을 해결하기 위한 방법이 Supervision이다.
SupervisionJob
일반적인 Job과 비슷하지만 Exception이 아래 방향으로만 전달된다. 즉 Exception이 발생했을 때 다른 자식들이나 부모의 Coroutine을 취소하지 않고 자신과 자신의 자식들의 Coroutine만 취소한다.
SupervisionJob은 매개변수로 parent: Job을 받는데 매개변수 이름에서 알 수 있듯 넘겨 받는 Job을 parent로 지정하며 취소되는 경우 SupervisionJob도 취소된다.
CoroutineScope(Dispatchers.IO + handler).launch {
val job1 = launch {
Log.d("TAETAEz", "job1 run")
launch {
Log.d("TAETAEz", "job1 child run")
delay(100L)
Log.d("TAETAEz", "job1 child finish")
}
delay(10L)
throw Exception("Hello World")
}
val job2 = launch {
delay(100L)
Log.d("TAETAEz", "job2 run")
}
val job3 = launch {
delay(100L)
Log.d("TAETAEz", "job3 run")
}
}
2022-01-16 04:28:37.852 13331-13357/? D/TAETAEz: job1 run
2022-01-16 04:28:37.855 13331-13356/? D/TAETAEz: job1 child run
2022-01-16 04:28:37.882 13331-13360/? D/TAETAEz: Exception Catch : java.lang.Exception: Hello World
위 예시를 보면 job1에서 Exception이 발생 후 job1 child, job2, job3 등 Coroutine이 취소되어 job2, job3 등의 Coroutine도 같이 취소됐고, 결과값이 안나왔다.
CoroutineScope(Dispatchers.IO + handler).launch {
val job1 = launch(SupervisorJob()) {
Log.d("TAETAEz", "job1 run")
launch {
Log.d("TAETAEz", "job1 child run")
delay(100L)
Log.d("TAETAEz", "job1 child finish")
}
delay(10L)
throw Exception("Hello World")
}
val job2 = launch {
delay(100L)
Log.d("TAETAEz", "job2 run")
}
val job3 = launch {
delay(100L)
Log.d("TAETAEz", "job3 run")
}
}
2022-01-16 04:31:49.301 13396-13424/? D/TAETAEz: job1 run
2022-01-16 04:31:49.306 13396-13425/? D/TAETAEz: job1 child run
2022-01-16 04:31:49.326 13396-13423/? D/TAETAEz: Exception Catch : java.lang.Exception: Hello World
2022-01-16 04:31:49.470 13396-13425/com.taetae98.coroutine D/TAETAEz: job3 run
2022-01-16 04:31:49.470 13396-13425/com.taetae98.coroutine D/TAETAEz: job2 run
하지만 위의 예시처럼 job1에 SupervisorJob()을 추가했을 때, job1의 Exception이 job2, job3에는 영향을 주지 않으며, job1의 자식들에게는 영향을 주면서 취소되는 것을 확인할 수 있다.
Supervisor Scope
해당 Scope에 있는 모든 Job들은 부모로 Exception을 전달하지 않습니다. 그렇기 때문에 job1에서 Exception이 발생해도 job2, job3이 실행됩니다. 여기서 주의할 점은 Exception Handling을 자식이 직접해야 해야합니다.
CoroutineScope(Dispatchers.IO).launch {
supervisorScope {
launch(handler) {
Log.d("TAETAEz", "job1 run")
throw Exception()
}
launch {
delay(100L)
Log.d("TAETAEz", "job2 run")
}
launch {
delay(100L)
Log.d("TAETAEz", "job3 run")
}
}
}
2022-01-16 04:43:23.335 13648-13676/com.taetae98.coroutine D/TAETAEz: job1 run
2022-01-16 04:43:23.338 13648-13676/com.taetae98.coroutine D/TAETAEz: Exception Catch : java.lang.Exception
2022-01-16 04:43:23.477 13648-13676/com.taetae98.coroutine D/TAETAEz: job3 run
2022-01-16 04:43:23.478 13648-13674/com.taetae98.coroutine D/TAETAEz: job2 run