안드로이드/Coroutine

Coroutine 예외처리

안드뽀개기 2023. 3. 8. 12:07
반응형

일반적인 예외처리

 일반적으로 예외처리는 try ~ catch 구문을 이용해서 처리할 수 있습니다.

suspend fun main(): Unit = runBlocking {
launch {
try {
delay(1000)
throw Exception("ERROR")
println("AAA")
} catch (e: Exception) {
println("Exception : $e")
}
}
launch {
delay(2000)
println("BBB")
}
}
view raw main.kt hosted with ❤ by GitHub

 

또한, 코틀린에서는 runCatching ~ onSuccess ~ onFailure 구문으로 처리 가능합니다.

suspend fun main(): Unit = runBlocking {
launch {
kotlin.runCatching {
delay(1000)
throw Exception("ERROR")
"AAA"
}.onSuccess {
println(it)
}.onFailure {
println("Exception : $it")
}
}
launch {
delay(2000)
println("BBB")
}
}
view raw main.kt hosted with ❤ by GitHub

 

위처럼 코드를 작성하면 예외처리를 할 수 있지만, try ~ catch 혹은 runCatching ~ onSuccess ~ onFailure 구문이 반복되어 보일러 플레이트 코드를 작성하게 된다.

 

위와 같은 문제를 해결하는 방법은 코루틴의 SupervisorJob 이다.

 


SupervisorJob

 SupervisorJob은 Coroutiine Child(코루틴 하위)에서 발생하는 Exception을 부모로 전파하지 않고 무시하도록 해준다. 

suspend fun main(): Unit = runBlocking {
launch { // 1번 scope
launch { // 2번 scope
println("AAA") // A
}
delay(1000)
throw Exception("ERROR") // error
println("BBB") // B
}
launch { // 3번 scope
delay(2000)
println("CCC") // C
}
}
// 출력
// AAA
// Exception in thread "main" java.lang.Exception: ERROR
view raw main.kt hosted with ❤ by GitHub

 

왜 A만 출력되고 B와 C는 출력되지 않았을까?

 -> error가 발생하여 자식 scope(2번 scope)에서 부모 scope(1번 scope)로 예외가 전파되어 runBlocking scope 전체가 cancel 되었기 때문이다.

 

 

 자식 scope에서 발생한 예외를 부모 scope로 전파하지 않도록 수정을 하면 다음과 같습니다.

suspend fun main(): Unit = runBlocking {
launch(SupervisorJob()) { // 1번 scope
launch { // 2번 scope
println("AAA") // A
}
delay(1000)
throw Exception("ERROR") // error
println("BBB") // B
}
launch { // 3번 scope
delay(2000)
println("CCC") // C
}
}
// 출력
// AAA
// Exception in thread "main" java.lang.Exception: ERROR
// CCC
view raw main.kt hosted with ❤ by GitHub

위의 예시를 보면 1번 scope의 CoroutineContext 파라미터에 SupervisorJob()을 넣어주고 실행했더니 error가 발생했지만 runBlocking scope가 취소되지 않아 C가 출력됨을 확인할 수 있습니다.


CoroutineExceptionHandler

예외가 발생했을 때 SupervisorSuper()CoroutineExceptionHandler를 함께 이용하면 예외발생 처리가 간편해집니다.

val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("exception : $exception")
}
suspend fun main(): Unit = runBlocking {
launch(SupervisorJob() + exceptionHandler) { // 1번 scope
launch { // 2번 scope
println("AAA") // A
}
delay(1000)
throw Exception("ERROR") // error
println("BBB") // B
}
launch { // 3번 scope
delay(2000)
println("CCC") // C
}
}
// 출력
// AAA
// exception : java.lang.Exception: ERROR
// CCC
view raw main.kt hosted with ❤ by GitHub

위 예시와 같이, CoroutineExceptionHandler scope에서 간편하게 예외를 처리할 수 있습니다.

 

 

val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("exception : $exception")
}
suspend fun main(): Unit = runBlocking(exceptionHandler) {
launch(SupervisorJob()) { // 1번 scope
launch { // 2번 scope
println("AAA") // A
}
delay(1000)
throw Exception("ERROR") // error
println("BBB") // B
}
launch(SupervisorJob()){ // 3번 scope
delay(2000)
throw Exception("ERROR2") // error2
println("CCC") // C
}
launch() { // 3번 scope
delay(3000)
println("DDD") // D
}
}
// 출력
// AAA
// exception : java.lang.Exception: ERROR
// exception : java.lang.Exception: ERROR2
// DDD
view raw main.kt hosted with ❤ by GitHub

또한, 위와 같이 부모 scope에 exceptionHandler를 넣어주면 여러 자식 scope에서 발생한 예외를 받아서 처리할 수 있습니다. 

반응형