ANR в розробці під Android — що це таке, причини та способи виправлення

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

ANR (Application Not Responding) — це системне повідомлення Android, яке з'являється, коли застосунок перестає реагувати на введення користувача довше ніж 5 секунд. За даними Android Developers, основна причина — довгі операції на головному потоці, що блокують обробку дотиків та відтворення інтерфейсу. Розуміння механізмів ANR необхідне кожному Android-розробнику для створення чуйних застосунків.

Головне

  • ANR — системне попередження Android при зависанні застосунку довше 5 секунд
  • Головний потік (UI-потік) — єдине місце, де блокування призводить до ANR
  • InputDispatcher — компонент системи, що фіксує затримку введення та ініціює ANR
  • traces.txt — ключовий файл для діагностики причини зависання на пристрої
  • StrictMode — вбудований інструмент Android для виявлення довгих операцій на UI-потоці

Що таке ANR

ANR (Application Not Responding) — це діалогове вікно операційної системи Android, яке з'являється, коли застосунок перестає відповідати на введення користувача. Система відстежує час обробки подій через InputDispatcher: якщо дотик або натискання кнопки не оброблено за 5 секунд, Android показує діалог із пропозицією закрити або зачекати застосунок.

Механізм ANR захищає користувацький досвід від завислих застосунків. Android не дозволяє одному застосунку блокувати всю систему — на відміну від Desktop-ОС, мобільна платформа примусово обмежує час обробки подій. BroadcastReceiver має ліміт 10 секунд, а служба foreground — 20 секунд.

ANR НЕ є винятком у коді — це системний механізм на рівні Linux-процесів. Android надсилає сигнал SIGQUIT процесу, після чого система зберігає стек викликів усіх потоків у файл traces.txt. Розробник отримує ANR не як catch-виняток, а як звіт після перезапуску застосунку. На Android 11+ з'явився API ApplicationExitInfo, який дозволяє програмно отримати причину завершення процесу, включаючи ANR — це спрощує збір статистики без ручного парсингу traces.txt.

Основні причини ANR

П'ять категорій операцій стабільно призводять до ANR в Android-застосунках. Кожна з них блокує головний потік, не даючи системі обробляти події введення та перемальовування екрана.

Мережеві запити на головному потоці

Синхронні HTTP-запити, виконані на UI-потоці, — найчастіша причина ANR у початківців. Навіть швидкий запит до сервера може зайняти 1–3 секунди, а при поганому з'єднанні — 30 секунд і більше. Android явно забороняє мережеві операції на головному потоці починаючи з API 11, викидаючи NetworkOnMainThreadException.

Використовуйте Coroutines або RxJava для асинхронних викликів. Корутини з диспетчером Dispatchers.IO виконують запит у фоновому потоці, а результат передають на головний через Dispatchers.Main. Це повністю виключає блокування UI-потоку мережевими операціями.

kotlin
fun fetchUserData() {
    CoroutineScope(Dispatchers.Main).launch {
        val result = withContext(Dispatchers.IO) {
            api.getUserData() // фонова операція
        }
        updateUI(result) // результат на головному потоці
    }
}

Інтенсивні обчислення на UI-потоці

Обробка великих масивів даних, парсинг JSON або XML, робота з bitmap безпосередньо на головному потоці — друга за частотою причина ANR. Навіть 300 мілісекунд безперервної роботи UI-потоку без повернення в цикл подій викликають помітну затримку відтворення, а граничне значення в 5 секунд фіксується як ANR.

WorkManager і фонові служби призначені для винесення важких обчислень із головного потоку. Використовуйте AsyncTask (застарілий), ListenableFuture або Kotlin Flow для передачі даних порціями, не блокуючи UI.

Блокування синхронізації та Deadlock

Deadlock виникає, коли два потоки утримують блокування і чекають один одного. Якщо один із потоків — головний, система фіксує ANR рівно через 5 секунд. Thread.join(), CountDownLatch.await() і synchronized-блоки, що викликаються з UI-потоку, несуть ризик блокування.

Уникайте будь-яких блокуючих операцій на головному потоці. Замість synchronized використовуйте ConcurrentHashMap, замість Thread.join() — корутини з async/await. Це правило діє для будь-якої мови в Android: Java, Kotlin або C++ через JNI.

Довга робота BroadcastReceiver

BroadcastReceiver виконується на головному потоці за замовчуванням. Якщо onReceive() зайнятий більше 10 секунд, Android показує ANR. Завантаження даних із БД або мережі всередині onReceive — гарантований шлях до зависання.

Використовуйте goAsync() всередині BroadcastReceiver для перемикання у фоновий потік або registerReceiver із getBackgroundBroadcastReceiver(). Це дозволяє обробляти події без блокування UI.

ContentProvider та SQLite на головному потоці

Важкі запити до ContentProvider або пряма робота з SQLite на UI-потоці — менш очевидна, але часта причина ANR. При міграції бази даних або bulk-вставці тисяч записів час виконання може перевищити 5-секундний ліміт.

Переносьте всі операції з базою даних у фонові потоки через Room із suspend-функціями. Room автоматично перевіряє, що запит не виконується на головному потоці, і викидає виняток при порушенні.

Як діагностувати ANR

Діагностика ANR відрізняється від налагодження звичайних винятків — ви не можете зловити ANR у try-catch. Основне джерело інформації — файл traces.txt, який Android створює в момент зависання.

traces.txt містить стек викликів усіх потоків застосунку на момент ANR. Щоб прочитати файл із реального пристрою, виконайте команду adb bugreport, яка збирає повний звіт системи, включаючи всі ANR за останній час. Для емулятора файл доступний у /data/anr/traces.txt. Стек викликів показує, який метод виконувався на головному потоці в момент блокування.

text
adb bugreport bugreport.zip
unzip -p bugreport.zip "*traces*" > traces.txt

Google Play Console надає розділ ANR & Crash із агрегованими звітами та частотами помилок. Для кожного ANR показано стек викликів та статистику за пристроями: модель, версія Android, регіон. Це дозволяє виявити ANR, які залежать від конкретних пристроїв або версій системи.

Android Studio з 2021 року містить ANR Watchdog у профілювальнику. Він автоматично записує дамп потоків, якщо головний потік не відповідає довше за пороговий час. Інструмент показує часову шкалу подій: які операції запускалися, які методи виконувалися і на якому етапі сталося блокування.

Як запобігти ANR

Профілактика ANR будується на одному фундаментальному правилі: головний потік повинен тільки обробляти події UI. Будь-яка операція довше 16 мілісекунд (час одного кадру) повинна виконуватися у фоновому потоці.

StrictMode — автоматична перевірка

StrictMode — вбудований інструмент Android для виявлення потенційних ANR на етапі розробки. Увімкніть його в Application.onCreate() із прапорцями на дискові та мережеві операції. При порушенні StrictMode викидає виняток або пише в logcat.

kotlin
if (BuildConfig.DEBUG) {
    StrictMode.ThreadPolicy.Builder()
        .detectDiskReads()
        .detectDiskWrites()
        .detectNetwork()
        .penaltyLog()
        .build()
        .let { StrictMode.setThreadPolicy(it) }
}

Асинхронні патерни: Coroutines та RxJava

Kotlin Coroutines — стандартний спосіб асинхронної роботи в сучасних Android-застосунках. Кореневий прийом: операції введення-виведення виконуються на Dispatchers.IO, результат передається на Dispatchers.Main для оновлення UI. Для Flow-подібних сценаріїв використовуйте Dispatchers.Default для CPU-інтенсивних завдань.

RxJava залишається популярним у легасі-проектах. subscribeOn(Schedulers.io()) та observeOn(AndroidSchedulers.mainThread()) — мінімальний набір для запобігання ANR. Головне правило те саме: жоден Observable або Flowable не повинен емітити дані з головного потоку.

Моніторинг у продакшені

Firebase Crashlytics з версії SDK 18.4.0 підтримує моніторинг ANR «з коробки». Для Android 11+ Crashlytics використовує системний API ApplicationExitInfo, який надає точну причину завершення: ANR, Crash або системне вбивство. Увімкніть спеціальні ключі з параметрами екрана та стану для контекстного аналізу.

Інструменти для виявлення ANR

П'ять інструментів покривають усі етапи роботи з ANR: від налагодження на робочій станції до моніторингу в продакшені. Кожен інструмент вирішує своє завдання та надає дані для різних сценаріїв.

ІнструментПризначенняФормат даних
StrictModeВиявлення на етапі розробкиLogcat / Exception
ANR Watchdog (Android Studio)Трасування в реальному часіThread dump + timeline
Google Play ConsoleАгрегована статистикаANR rate + stack traces
Firebase CrashlyticsПродакшен-моніторингApplicationExitInfo
adb bugreportПовний системний звітtraces.txt + logcat + dmesg

Кожен інструмент має свою нішу: StrictMode ловить очевидні порушення на ранніх етапах, Crashlytics показує реальну частоту ANR у користувачів, а adb bugreport дає максимально повну картину для складних випадків. Комбінуйте їх для повного покриття.

Firebase Performance Monitoring

Firebase Performance відстежує час відгуку UI-потоку та автоматично створює трейси для підозріло довгих операцій. Якщо головний потік блокується більш ніж на 500 мс, Performance фіксує спеціальний trace з іменем методу-винуватця. Це дозволяє виявляти ANR-сценарії без участі користувача та до того, як вони стануть критичними.

Інтеграція з Firebase Crashlytics дає повну картину: Performance показує уповільнення до ANR, а Crashlytics — сам факт зависання. Налаштуйте оповіщення в Firebase Console на подію ANR rate вище 0.1%, і ви будете отримувати повідомлення про нові проблеми до масових скарг користувачів.

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

Чим ANR відрізняється від Crash?

ANR — це зависання, при якому застосунок не відповідає, але залишається в пам'яті. Crash — повне аварійне завершення з виходом із процесу. ANR можна «пережити», якщо система або користувач дочекаються відповіді, а Crash завжди завершує застосунок.

Чи можна зловити ANR через try-catch?

Ні. ANR — не виняток Java/Kotlin, а сигнал системи на рівні процесів (SIGQUIT). Розробник не може обробити його в коді застосунку. Єдиний спосіб реагувати на ANR — аналізувати звіти після перезапуску.

Чому ANR з'являється на одних пристроях, але не на інших?

Продуктивність пристрою, версія Android, завантаженість CPU та кількість фонових процесів впливають на ймовірність ANR. На слабких пристроях та сама операція може виконуватися в 2–3 рази довше, перевищуючи 5-секундний ліміт.

Який ліміт часу у BroadcastReceiver до ANR?

10 секунд для звичайного BroadcastReceiver в onReceive(). Для foreground-служб ліміт становить 20 секунд, а для ContentProvider — немає явного ліміту, але блокування головного потоку довше 5 секунд все одно викликає ANR.

Що робити, якщо ANR відбувається рідко і не відтворюється?

Увімкніть StrictMode у всіх debug-збірках, додайте моніторинг через Firebase Crashlytics та використовуйте adb bugreport, коли ANR трапляється. Нерегулярні ANR часто пов'язані з умовами гонки (race condition) або специфічними станами мережі.

Підсумки

  • ANR — системний механізм Android, що спрацьовує при блокуванні головного потоку довше 5 секунд
  • Головний потік повинен займатися тільки UI — всі інші операції виносяться у фонові потоки
  • Діагностика ANR ведеться через traces.txt, Google Play Console та Firebase Crashlytics
  • StrictMode виявляє потенційні ANR на етапі розробки без запуску на реальному пристрої
  • Coroutines з Dispatchers.IO — стандартний спосіб асинхронної роботи в сучасних Android-проектах
  • BroadcastReceiver потребує goAsync() або фонового реєстратора для роботи довше 10 секунд
  • ANR у продакшені моніториться через Crashlytics та вбудований API ApplicationExitInfo на Android 11 і вище

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

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

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

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