Как обрабатываются ошибки
Что проверяют
Вопрос открытый, и интервьюер смотрит, на каком уровне вы о нём думаете: только try-catch или весь путь ошибки от сети до экрана. Следом обычно спрашивают, что происходит с исключением внутри корутины и почему приложение упало, хотя вызов обёрнут в try.
Как отвечать
Постройте ответ по слоям приложения на Kotlin:
- источник: Retrofit с suspend-функцией, которая возвращает тело, бросает
HttpExceptionна код ответа вне 2xx иIOExceptionпри проблемах соединения. Нужен код без исключения, возвращайтеResponse<T>; - слой данных: превращает технические исключения в доменные ошибки, например sealed-класс с вариантами
Network,Unauthorized,NotFound. Выше этого слояHttpExceptionне уходит; - ViewModel: переводит результат в состояние экрана,
UiState.Errorс понятным текстом и кнопкой повтора. УFlowдля этого есть операторcatch, он ловит ошибки только выше себя по цепочке; - корутины: необработанное исключение в
launchотменяет родительский scope, вasyncоно всплывает наawait().CoroutineExceptionHandlerсрабатывает только на корневой корутине, аsupervisorScopeне даёт падению одной дочерней отменить соседние; - наблюдаемость: необработанные падения собирает Crashlytics, а важные обработанные ошибки отправляют как non-fatal через
recordException.
В Kotlin нет checked-исключений, компилятор не напомнит, что функция может бросить. Поэтому договорённость о том, какой слой что ловит, держит команда.
Где спотыкаются
Ловят Exception внутри корутины и заодно глотают CancellationException, после чего корутина перестаёт реагировать на отмену. Та же ловушка у runCatching: отмену из его результата нужно пробрасывать дальше.
Ставят try-catch вокруг launch и удивляются падению. Исключение выбрасывается внутри корутины, а не в месте вызова launch, и этот catch его не видит.
Ловят Throwable и продолжают работу после OutOfMemoryError. Такую ошибку лучше донести до краш-репорта, чем оставить приложение в сломанном состоянии.