З'єднання обривається — типові причини та методи вирішення

Автор: IT Sectr Опубліковано: 2026-07-29 Час читання: 10 хв

Втрата з'єднання — одне з найпоширеніших і найдратівливіших явищ у мобільних застосунках. Користувач втрачає доступ до даних, операція переривається, застосунок зависає або вилітає. За даними Google Android Developer Blog, 70% користувачів видаляють застосунок, якщо він вилітає або зависає двічі. Розберемо причини втрати з'єднання та способи побудови відмовостійких комунікацій.

Головне

  • ANR (Application Not Responding) — блокування UI-потоку довше 5 секунд призводить до примусового завершення
  • Offline-first — архітектура, де локальне сховище — джерело істини, а мережа — механізм синхронізації
  • Retry with backoff — автоматичний повтор запиту зі збільшенням затримки при мережевих помилках
  • ConnectivityManager — Android API для відстеження стану мережі та адаптації поведінки застосунку
  • Graceful degradation — застосунок має працювати (хоча б частково) при відсутності мережі

Що означає «обривається» в мобільних застосунках?

Обривається — користувацький термін, що описує ситуацію, коли застосунок втрачає з'єднання з сервером, перестає відповідати на дії або завершується з помилкою. У технічному сенсі це може бути: мережева помилка (таймаут, DNS failure), ANR (блокування UI-потоку), crash (необроблений виняток) або race condition (стан гонки).

Для користувача всі ці сценарії виглядають однаково: застосунок перестає працювати. Різниця для розробника — у підході до діагностики та виправлення. Мережеві помилки вирішуються retry-механізмами, ANR — винесенням операцій з UI-потоку, crash — exception handling.

За даними Crittercism (тепер Apteligent), в середньому мобільний застосунок втрачає 1-2% користувачів при кожному падінні. Для застосунку з 1 млн користувачів це 10-20 тисяч втрачених встановлень на один баг. Особливо критично для застосунків у фінансовому та медичному секторах.

Основні причини втрати з'єднання

Нестабільна мережа — мобільні пристрої постійно перемикаються між Wi-Fi та мобільною мережею, заходять у зони без покриття (метро, ліфт, підвал). Кожне перемикання викликає тимчасову втрату з'єднання, яку застосунок має обробляти коректно.

Таймаути — якщо сервер не відповідає протягом встановленого таймауту (зазвичай 10-30 секунд), клієнт кидає SocketTimeoutException. Довгі таймаути без зворотного зв'язку сприймаються користувачем як зависання. Рекомендується встановлювати таймаут не більше 15 секунд.

kotlin
val client = OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(15, TimeUnit.SECONDS)
    .writeTimeout(15, TimeUnit.SECONDS)
    .retryOnConnectionFailure(true)
    .build()

Стан гонки (race condition) — виникає, коли кілька потоків одночасно читають і пишуть одні й ті самі дані без синхронізації. Наприклад, завантаження даних з кешу в UI-потік паралельно з оновленням кешу з мережі може призвести до відображення застарілих або некоректних даних.

  • Необроблені винятки в callback або корутині призводять до crash застосунку
  • Memory pressure — system вбиває застосунок при нестачі пам'яті для foreground-застосунку
  • Lifecycle race — async операція завершується після того, як Activity/Fragment знищений
  • Блокування UI — виконання мережі або БД на головному потоці викликає ANR через 5 секунд

Архітектура для відмовостійких застосунків

Offline-first — архітектурний патерн, при якому локальне сховище (Room, CoreData) є єдиним джерелом істини. Мережа використовується для синхронізації даних у фоні. Користувач завжди бачить актуальні дані з локального кешу, навіть при відсутності мережі.

Repository pattern — єдина точка входу для даних, яка вирішує, брати дані з мережі чи з кешу. Репозиторій абстрагує джерело даних від ViewModel та UI. При мережевій помилці репозиторій автоматично перемикається на локальне джерело.

kotlin
class UserRepository(
    private val api: UserApi,
    private val dao: UserDao
) {
    suspend fun getUsers(): Result<List<User>> {
        return try {
            val remote = api.fetchUsers()
            dao.insertAll(remote)
            Result.success(remote)
        } catch (e: IOException) {
            val cached = dao.getAll()
            if (cached.isNotEmpty()) {
                Result.success(cached) // return cache on network error
            } else {
                Result.failure(e)
            }
        }
    }
}

Circuit Breaker — патерн захисту сервера від лавини запитів при недоступності. Після N послідовних помилок вимикач розмикається, і всі запити одразу повертають помилку без спроби з'єднання. Через заданий таймаут вимикач переходить у напіввідкритий стан для пробного запиту.

Як обробляти мережеві помилки?

Exponential backoff — стандартний механізм retry. Після першої невдачі зачекати 1 секунду, після другої — 2 секунди, потім 4, 8, 16. Обмежте максимальну кількість спроб (зазвичай 3-5), щоб не перевантажувати сервер та батарею.

User feedback — при мережевій помилці покажіть зрозуміле повідомлення: «Немає з'єднання», «Сервер тимчасово недоступний», «Перевірте інтернет». Використовуйте Snackbar або Inline State View. Ніколи не показуйте технічні помилки (HTTP 500, SocketException) користувачу.

ConnectivityManager — Android API для моніторингу мережі. Дозвольте застосунку реагувати на зміни: при втраті мережі показувати placeholder, при відновленні — автоматично оновлювати дані. У iOS використовуйте NWPathMonitor з Network framework.

kotlin
class NetworkMonitor(private val context: Context) {
    private val manager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    fun isOnline(): Boolean {
        val network = manager.activeNetwork ?: return false
        val caps = manager.getNetworkCapabilities(network) ?: return false
        return caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    }
}

Інструменти моніторингу та логування

Crashlytics (Firebase) — стандартний crash-reporting інструмент для мобільних застосунків. Збирає stacktrace всіх необроблених винятків, версію ОС, модель пристрою та час падіння. Дозволяє групувати помилки та призначати відповідальних за фікси.

Sentry — альтернатива Crashlytics з підтримкою performance monitoring. Дозволяє трейсити конкретні транзакції (наприклад, «авторизація користувача») та бачити, на якому кроці сталася помилка. Performance tracing допомагає відрізняти мережеві таймаути від багів у логіці застосунку.

Timber — бібліотека логування для Android з автоматичним додаванням тегів за класом. У debug-збірці логуйте всі мережеві запити та відповіді. У release-збірці — лише помилки та попередження через Crashlytics.setCustomLog.

ІнструментТипКоли використовувати
CrashlyticsCrash reportingЗавжди в release — автоматичний збір крашів
SentryCrash + PerformanceКоли потрібно профілювати конкретні користувацькі сценарії
TimberLoggingDebug: повне логування; Release: лише помилки
HTTP ToolkitNetwork debugЛокальне перехоплення та аналіз HTTP-трафіку

За даними Firebase Summit 2023, застосунки, що впровадили Crashlytics + Performance Monitoring, скорочують середній час виявлення та виправлення критичних багів з 3 днів до 4 годин. Рекомендується налаштувати алерти на кожен crash з частотою >0.1% від активних користувачів.

Часті запитання

Що робити, якщо застосунок вилітає без помилки?

Якщо crash не ловиться в Crashlytics, перевірте native crash (SIGSEGV, SIGABRT) — вони не обробляються Java/Kotlin exception handler. В Android це може бути витік native memory з JNI, в iOS — EXC_BAD_ACCESS. Використовуйте Breakpad (Android) або PLCrashReporter (iOS) для збору native crash stacktrace.

Як відтворити баг, який проявляється лише при поганій мережі?

Використовуйте Network Link Conditioner (вбудований в iOS, для Android є Facebook Network Connection Class або налаштування Developer Options > Network > Select network type). Встановіть затримку 500-3000 ms та втрату пакетів 5-30%. Також можна використовувати Charles Proxy або mitmproxy для емуляції мережевих затримок і розривів.

Як запобігти ANR при мережевих запитах?

ANR виникає, якщо UI-потік заблокований довше 5 секунд. Мережеві запити мають виконуватися у фоновому потоці: корутини (viewModelScope.launch(Dispatchers.IO)), RxJava (subscribeOn(Schedulers.io)) або WorkManager для синхронізації. Завжди встановлюйте таймаути на HTTP-клієнті — відсутність таймауту може призвести до вічної блокування.

Що таке race condition і як його уникнути?

Race condition — ситуація, коли результат операції залежить від порядку виконання потоків. Наприклад, користувач швидко натискає кнопку «Надіслати» двічі, і запит іде двічі. Рішення: використовуйте Mutex, single-threaded executors або state machine (відключіть кнопку після першого натискання). В Kotlin використовуйте Mutex з корутин або @Synchronized анотацію.

Як тестувати відмовостійкість застосунку?

Застосовуйте Chaos Engineering для мобільних застосунків: відключайте мережу під час операцій, симулюйте високу затримку, перемикайте між Wi-Fi та мобільною мережею, вбивайте процес системою. Інструменти: Facebook Network Connection Class, Charles Proxy, iOS Network Link Conditioner. В CI/CD додавайте UI-тести з різними мережевими умовами через AndroidTest Orchestrator.

Підсумки

  • Обривається — збірний термін для мережевих помилок, ANR, crash та race conditions; користувацький досвід однаковий, причини різні
  • Мережеві помилки — найчастіша причина; рішення включає таймаути (10-15 секунд), exponential backoff та offline-first архітектуру
  • ANR виникає при блокуванні UI-потоку довше 5 секунд; мережеві та дискові операції завжди виконуйте у фоновому потоці
  • Offline-first з Repository pattern: локальне сховище — джерело істини, мережа — механізм синхронізації
  • Crashlytics + Performance Monitoring — мінімальний набір для production моніторингу з алертами на часті краші
  • Стан гонки вимагає синхронізації потоків: Mutex, State Machine або однопотоковий екзек'ютор
  • Тестуйте з емуляцією поганої мережі та Chaos Engineering — тільки так можна виявити проблеми, приховані в ідеальних умовах розробки

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також