Как обрабатывать ошибки в сетевых запросах
Что проверяют
Умеете ли вы разделять ошибки по природе и показывать пользователю осмысленное состояние, а не падение или вечный лоадер. Следом спрашивают, где в архитектуре живёт обработка и как устроены повторы.
Как отвечать
Типичный стек: Retrofit с suspend-функциями, OkHttp под ним, Kotlin Serialization или Moshi. Ошибки делятся на классы:
- сетевые:
IOExceptionи наследники,UnknownHostException,SocketTimeoutException. Нет сети, таймаут, обрыв; - HTTP-ошибки: у
suspend fun getUser(): Userпри коде вне 2xx Retrofit бросаетHttpException. Нужны код и тело ошибки: объявите типResponse<User>и проверяйтеisSuccessful; - ошибки разбора: сервер ответил 200, но JSON не совпал с моделью;
- отмена:
CancellationExceptionпри уходе с экрана. Её нельзя глотать, иначе ломается структурная конкурентность.
Репозиторий оборачивает вызов в sealed-класс результата (Success, Error с типом ошибки) и переводит исключения в доменные ошибки. ViewModel делает из этого состояние экрана в StateFlow: загрузка, данные, ошибка с кнопкой «Повторить». UI про HTTP-коды не знает.
Сквозное живёт в OkHttp: Interceptor для логирования и заголовков, Authenticator для обновления токена на 401. Повторы с экспоненциальной задержкой только для идемпотентных запросов и временных ошибок (таймаут, 503), фоновую отправку отдайте WorkManager с ограничением на сеть.
Где спотыкаются
Ловят Exception целиком и вместе с ним CancellationException: корутина продолжает работу после отмены. runCatching делает ту же ошибку незаметной.
Повторяют любой запрос, включая POST с оплатой. Без ключа идемпотентности пользователь получит двойное списание.
Показывают сырой текст исключения или «Ошибка 500» и не отличают «нет интернета» от «сервер сломался».