Я полагаю, что ответ находится в этом разделе из официальных документов сопрограмм :
Если сопрограмма встречает исключение, отличное от CancellationException, она отменяет своего родителя с этим исключением.Это поведение не может быть переопределено и используется для обеспечения стабильных иерархий сопрограмм для структурированного параллелизма, которые не зависят от реализации CoroutineExceptionHandler.Исходное исключение обрабатывается родителем, когда завершаются все его дочерние элементы.
Это также причина, по которой в этих примерах CoroutineExceptionHandler всегда устанавливается в сопрограмму, созданную в GlobalScope. Не имеет смысла устанавливать обработчик исключений для сопрограммы, которая запускается в области основного runBlocking, поскольку основная сопрограмма будет всегда отменяться, когда ее дочерний элемент завершается с исключением, несмотря на установленный обработчик .
(выделение мое)
То, что здесь описано, относится не только к runBlocking
и GlobalScope
, но и к любому создателю сопрограмм не верхнего уровня и пользовательской области действия.
Для иллюстрации:
fun f() = runBlocking {
val h1 = CoroutineExceptionHandler { _, e ->
trace("handler 1 e: $e")
}
val h2 = CoroutineExceptionHandler { _, e ->
trace("handler 2 e: $e")
}
val cs = CoroutineScope(newSingleThreadContext("t1"))
trace("launching j1")
val j1 = cs.launch(h1) {
delay(1000)
trace("launching j2")
val j2 = launch(h2) {
delay(500)
trace("throwing exception")
throw RuntimeException("error!")
}
j2.join()
}
trace("joining j1")
j1.join()
trace("exiting f")
}
f()
Вывод:
[main @coroutine#1]: launching j1
[main @coroutine#1]: joining j1
[t1 @coroutine#2]: launching j2
[t1 @coroutine#3]: throwing exception
[t1 @coroutine#2]: handler 1 e: java.lang.RuntimeException: error!
[main @coroutine#1]: exiting f
Обратите внимание, что обработчик h1
выполнен, а h2
- нет.Это аналог обработчика при выполнении GlobalScope#launch
, но не обработчик, предоставленный любому launch
внутри runBlocking
.
TLDR
Обработчики, предоставленные некорневым сопрограммамиз области будет игнорироваться.Будет выполнен обработчик, предоставленный для корневой сопрограммы.
Как правильно указал Марко Топольник в комментариях ниже, приведенное выше обобщение применимо только к сопрограммам, созданным launch
.Созданные async
или produce
всегда будут игнорировать все обработчики.