Какие проблемы возникают при оборачивании launch в try-catch в Kotlin Coroutines
Что проверяют
Понимаете ли вы, что launch возвращается сразу, а исключение внутри него идёт не вверх по стеку вызова, а вверх по иерархии Job. Следом обычно спрашивают, где тогда ловить ошибку и что произойдёт в viewModelScope.
Как отвечать
Внешний try-catch вокруг launch не ловит ошибки из тела корутины, даже если оно начало выполняться сразу на Dispatchers.Main.immediate: launch только запускает корутину и отдаёт Job. Дальше исключение:
- идёт к родителю: обычный
Jobотменяет родителя и всех соседних детей, то есть весь scope. ВviewModelScopeиlifecycleScopeстоитSupervisorJob, соседи не отменяются, но исключение всё равно остаётся необработанным; - ищет обработчик: если в корневой корутине или в контексте scope есть
CoroutineExceptionHandler, он получит исключение. Если нет, оно уходит вThread.UncaughtExceptionHandler, и на Android приложение падает.
Где ловить правильно:
- внутри корутины: try-catch вокруг конкретного
suspend-вызова, например запроса в сеть, с переводом ошибки в состояние UI; - через
coroutineScope { }: он пробрасывает ошибку детей наружу обычным исключением, и try-catch вокруг него работает.supervisorScopeпозволяет детям падать независимо; CoroutineExceptionHandler: последний рубеж для логирования. Работает только в корневой корутине или в scope, на вложенномlaunchего игнорируют;- Flow: оператор
catchперехватывает ошибки выше по цепочке.
Про async: исключение приходит при await(), но если async дочерний для обычного Job, родитель отменится сразу. Так что это не замена launch для обработки ошибок.
Где спотыкаются
Ловят Exception внутри корутины и глотают CancellationException. Корутина перестаёт реагировать на отмену и продолжает работать после ухода с экрана. Отмену нужно пробрасывать дальше, и это же касается runCatching.
Вешают CoroutineExceptionHandler на вложенный launch и удивляются, что он молчит.
Думают, что SupervisorJob в viewModelScope спасает от падения. Он защищает только соседние корутины от отмены, а необработанное исключение всё равно роняет приложение.