https://kotlinlang.org/docs/exception-handling.html
์ ๋งํฌ๋ฅผ ๊ณต๋ถํ๋ฉฐ ๊ธฐ๋กํ ๊ธ์ด๋ค
CoroutineExceptionHandler
์ต์ ์ ํธ๋ค๋ฌ๋ launch์ ๊ฐ์ ์ฝ๋ฃจํด๋น๋์์ ๋ฐ์ํ๋ uncaught ์์ธ์ ๋์ํ ์์๋ค. async์ ๊ฐ์ด ๋ชจ๋ ์์ธ๋ฅผ ์ฌ์ฉ์ ๋จ์์ ์ฒ๋ฆฌํ ์ ์๋ ๊ฒฝ์ฐ์๋ ์ด ํธ๋ค๋ฌ๊ฐ ํจ๊ณผ๊ฐ ์๋ค.
@OptIn(DelicateCoroutinesApi::class)
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) { // root coroutine, running in GlobalScope
throw AssertionError()
}
val deferred = GlobalScope.async(handler) { // also root, but async instead of launch
throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await()
}
joinAll(job, deferred)
}
Globalscope์์ root ์ฝ๋ฃจํด ์ค์ฝํ๋ก ์ฐ๊ธฐ์ํด @OptIn ์ด๋ ธํ ์ด์ ์ด ํ์ํ๋ค. ์ด๋ฌ๋ฉด job์์ ๋ฐ์ํ uncaught ์์ธ๋ฅผ ํธ๋ค๋ฌ์์ ์ฒ๋ฆฌํด์ค๋ค.
Cancellation and Exceptions
์ทจ์๋ ์์ธ์ ์ ์ฌํ๋ค๊ณ ํ ์ ์๋ค. ์ทจ์๋ฅผ ํ ๋ ์ฝ๋ฃจํด์ CancellationException์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ธ๋ฐ, ์ด ์์ธ๋ ๋ฐ๋ก ํธ๋ค๋ฌ์์ ์ฒ๋ฆฌ๋์ง์๊ณ ๊ทธ๋ฅ ๋๋ฒ๊ทธ ์ ๋ณด๋ก ํ์ฉ๋๋ค. ์ ํ๋์ง์๋ ์์ธ์ฒ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์ ๋ฃจํธ ์ฝ๋ฃจํด ์ค์ฝํ๊น์ง ์ ์ฉ๋์ง์๊ณ cancel์ด ๋ถ์ด์๋ ์ค์ฝํ๋ง ์ทจ์์ํจ๋ค.
ํ๋ ๋ณผ๋งํ ์ฝ๋๊ฐ ์๋ค.
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) {
launch { // the first child
try {
delay(Long.MAX_VALUE)
} finally {
withContext(NonCancellable) {
println("Children are cancelled, but exception is not handled until all children terminate")
delay(100)
println("The first child finished its non cancellable block")
}
}
}
launch { // the second child
delay(10)
println("Second child throws an exception")
throw ArithmeticException()
}
}
job.join()
Long.MAX_VALUE๋ฉด ์ผ๋ฐ์ ์ธ ์ํฉ์์ ๊ต์ฅํ ๊ธด ์๊ฐ์ธ๋ฐ uncaught ์์ธ๋ฅผ ๋์ ธ์ ๊ฐ์ ๋ก ์ค์ฝํ๋ฅผ ํ์ถ์ํจ๋ค.
์ด๋ withContext๋ ๋ฐ๋ก ์์ง์ฌ์ ์ด ์ค์ฝํ๊ฐ ๋๋์ผ ํธ๋ค๋ฌ์ ์๋ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ค.
Exceptions aggregation
์์ธ์ฒ๋ฆฌ ๊ธฐ๋ณธ์ ์ ์ผ ์ฒ์์ ๋ฐ์ํ ๊ฒ์ ๋จผ์ ์ฒ๋ฆฌํ๋ค๋ ๊ฐ๋ ์ด๋ค.
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
val job = GlobalScope.launch(handler) {
val inner = launch { // all this stack of coroutines will get cancelled
launch {
launch {
throw IOException() // the original exception
}
}
}
try {
inner.join()
} catch (e: CancellationException) {
println("Rethrowing CancellationException with original cause")
throw e // cancellation exception is rethrown, yet the original IOException gets to the handler
}
}
job.join()
join์์ ๋ฐ์ํ๋ IOException์ ํธ๋ค๋ฌ๊ฐ ๋จผ์ ๊ฐ์ ธ๊ฐ๋ค.
Supervision
SupervisorJob์ ์ฌ์ฉํด ์คํจํ ์์ ๋ง ๋ค์ ์์ํ๋ ๊ฒ๊ณผ ๊ฐ์ ์์ ์ ์ฒ๋ฆฌํ ์ ์๋ค.
fun main() = runBlocking {
val supervisor = SupervisorJob()
with(CoroutineScope(coroutineContext + supervisor)) {
// launch the first child -- its exception is ignored for this example (don't do this in practice!)
val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) {
println("The first child is failing")
throw AssertionError("The first child is cancelled")
}
// launch the second child
val secondChild = launch {
firstChild.join()
// Cancellation of the first child is not propagated to the second child
println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active")
try {
delay(Long.MAX_VALUE)
} finally {
// But cancellation of the supervisor is propagated
println("The second child is cancelled because the supervisor was cancelled")
}
}
// wait until the first child fails & completes
firstChild.join()
println("Cancelling the supervisor")
supervisor.cancel()
secondChild.join()
}
}
์ํผ๋ฐ์ด์ ๋ฅผ ์บ์ฌํ๋๋ฐ secondChild๋ก ์ ํ๋๋ ๋ชจ์ต์ด ๋ณด์ธ๋ค.
fun main() = runBlocking {
try {
supervisorScope {
val child = launch {
try {
println("The child is sleeping")
delay(Long.MAX_VALUE)
} finally {
println("The child is cancelled")
}
}
// Give our child a chance to execute and print using yield
yield()
println("Throwing an exception from the scope")
throw AssertionError()
}
} catch(e: AssertionError) {
println("Caught an assertion error")
}
}
supervisorScope๋ฅผ ์ฌ์ฉํ ์๋ ์๋๋ฐ, uncaught์์ธ๊ฐ ๋ฃจํธ๊น์ง ๋ฐ๋ก ์ํผ์ง๊ฒ ํ๋ค. ์๋ ์์ธ๊ฐ ์๋ฐฉํฅ์ด์๋ค๋ฉด ์ํผ๋ฐ์ด์ ์ค์ฝํ์์๋ ๋จ๋ฐฉํฅ์ผ๋ก ์ ์ฉ๋ผ์ AssertionError๊ฐ ๋ฐ๋ก runBlocking์ผ๋ก ๊ฐ์ง์๊ณ supervisorScope์์ ๋จผ์ ์ฒ๋ฆฌ๋๊ณ , ๋ถ๋ชจ์ค์ฝํ๋ก ๊ฐ๋ค.
๋ฐ๋ผ์ ์ํผ๋ฐ์ด์ ์ค์ฝํ์์ ์ง์ ์์ธ์ฒ๋ฆฌ๋ฅผ ํ๋ ค๋ฉด handler๋ฅผ ์ง์ ๋ฃ์ด์ ์ฒ๋ฆฌํ๋ฉด ๋๋ค. ์ด๋ฌ๋ฉด ๋ฃจํธ์ค์ฝํ์ฒ๋ผ ์๋ํ๋ค.
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, exception ->
println("CoroutineExceptionHandler got $exception")
}
supervisorScope {
val child = launch(handler) {
println("The child throws an exception")
throw AssertionError()
}
println("The scope is completing")
}
println("The scope is completed")
}
"๋๊ธ, ๊ณต๊ฐ ๋ฒํผ ํ ๋ฒ์ฉ ๋๋ฅด๊ณ ๊ฐ์ฃผ์๋ฉด ํฐ ํ์ด ๋ฉ๋๋ค"
'Android ๐ฅ๏ธ > Coroutine๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
emit๊ณผ value assignment ์ฐจ์ด์ (feat. SharedFlow, StateFlow) (0) | 2024.08.02 |
---|---|
Callback ๊ธฐ๋ฐ์ ๋น๋๊ธฐ์์ ์ ์ฌ์ฉ๋๋ CompletableDeferred, suspendCancellableCoroutine (0) | 2024.05.05 |
Asynchronous Flow - Intermediate flow operators~ (1) | 2024.03.29 |
Coroutine context and dispatchers (0) | 2024.03.29 |
Coroutine Composing suspending functions (0) | 2024.03.29 |