Чому ANR небезпечні для вашого додатку?
Уявіть: ваш додаток зависає на 5 секунд, користувач бачить чорний екран і діалог закриття. В Crashlytics приходить ANR без стеку — тільки ім'я Activity. Причина невідома. Так втрачаються користувачі та рейтинг у Google Play. Згідно з офіційною документацією, ANR (Application Not Responding) виникає, коли головний потік блокується довше 5 секунд на input-подію або 10 секунд для BroadcastReceiver.
| Тип події | Поріг |
|---|---|
| Input-подія | 5 с |
| BroadcastReceiver | 10 с |
| Service (startService) | 20 с |
Користувач бачить чорний екран і діалог «Додаток не відповідає», після чого система завершує процес. У Crashlytics з'являється запис без стеку — тільки ім'я ANR і Activity. Це гірше крэша: без трейсу ви не знаєте причину. Ми налаштовуємо ANR-моніторинг під ключ: підключаємо Firebase Crashlytics або Sentry, читаємо ApplicationExitInfo для точних трейсів та налаштовуємо алерти в Slack. У результаті ви бачите, який код викликав зависання, і усуваєте його до масових скарг. Економія часу на налагодженні — до 50%. Докладніше про ANR можна прочитати в документації Android. Оцінимо ваш проект безкоштовно — напишіть нам.
Звідки беруться ANR
Найчастіше це не один важкий виклик, а ланцюжок. Наприклад, корутина на Dispatchers.Main викликає runBlocking, всередині якого — Room.database.query() без suspend. На слабкому пристрої з зайнятим диском це 5+ секунд блокування.
Друге за частотою джерело — SharedPreferences при холодному старті. На старих версіях Android на бюджетних пристроях перше читання з SharedPreferences блокує main thread до 800 мс, якщо файл не в page cache.
// Антипатерн — читання SharedPreferences на main thread при старті class SplashActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val token = getSharedPreferences("prefs", MODE_PRIVATE) .getString("auth_token", null) // блокує main thread } } Правильно: винести в DataStore (корутинний) або читати в lifecycleScope.launch(Dispatchers.IO).
Які інструменти для моніторингу ANR кращі?
Порівняємо три популярні рішення:
| Інструмент | Метод детекції | Складність | Точність трейсів |
|---|---|---|---|
| Firebase Crashlytics | Watchdog + ApplicationExitInfo (API 30+) | Низька (один рядок) | Обмежена (не завжди стек) |
| Sentry | Watchdog з налаштовуваним таймаутом | Середня (конфігурація) | Висока (повний стек) |
| Android Vitals | Агрегація Google Play | Без SDK | Дані по ANR Rate, без стеку |
Firebase Crashlytics — найпростіший шлях, якщо він уже підключений. Але для глибокого аналізу використовуйте Sentry або ApplicationExitInfo. Sentry дає на 70% більше інформативних трейсів, ніж Crashlytics. ApplicationExitInfo точніший за watchdog: він зберігає повний thread dump в момент ANR, а не тільки поточний стек.
Як налаштувати Crashlytics ANR
- Додайте залежність у
build.gradle:
implementation("com.google.firebase:firebase-crashlytics:18.+") - Ініціалізуйте SDK у
Application.onCreate(). - Crashlytics автоматично реєструє ANR через
ApplicationExitInfoна API 30+ або через власний watchdog на старіших версіях.
Як налаштувати Sentry ANR
SentryAndroid.init(this) { options -> options.dsn = "https://[email protected]/project" options.isAnrEnabled = true options.anrTimeoutIntervalMillis = 5000 options.isAnrReportInDebug = false // не спамим у debug } Sentry запускає watchdog-потік, який кожні 1000 мс перевіряє, чи живий main thread. Якщо немає відповіді довше anrTimeoutIntervalMillis — знімає стек і відправляє подію.
ApplicationExitInfo — точне джерело трейсів
На Android 10+ система зберігає причину завершення процесу в ApplicationExitInfo. Це в 3 рази точніше за watchdog-потоки:
val activityManager = getSystemService(ActivityManager::class.java) val exitReasons = activityManager.getHistoricalProcessExitReasons(null, 0, 10) exitReasons.filter { it.reason == ApplicationExitInfo.REASON_ANR }.forEach { info -> info.traceInputStream?.use { stream -> val trace = stream.bufferedReader().readText() // відправляємо trace у ваш моніторинг Log.e("ANR", trace) } } traceInputStream містить повний thread dump — ті ж дані, що в /data/anr/traces.txt. Можна прочитати при наступному запуску і відправити в Sentry або Datadog як attachment.
Діагностика через StrictMode
У debug-збірці StrictMode допомагає зловити потенційні ANR до продакшену:
if (BuildConfig.DEBUG) { StrictMode.setThreadPolicy( StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .penaltyFlashScreen() .build() ) StrictMode.setVmPolicy( StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .build() ) } penaltyFlashScreen() підсвічує екран червоним при кожному диск-читанні на main thread — розробник бачить проблему негайно.
Налаштування алертів
У Firebase Crashlytics налаштуйте velocity alert на ANR:
- Поріг: > 1% crash-free sessions affected за 1 годину.
- Канал: Slack webhook через Firebase Alert Channels.
У Sentry — через Issue Alerts з умовою event.type:transaction AND event.tags.mechanism:ANR.
Що входить у налаштування ANR-моніторингу
- Підключення ANR-детектора (Firebase Crashlytics або Sentry, або обидва)
- Налаштування читання
ApplicationExitInfoна API 30+ для точних трейсів - Включення StrictMode у debug-збірці для превентивної діагностики
- Конфігурація velocity alerts з нотифікацією в Slack
- Аналіз Android Vitals baseline для порівняння з конкурентами
- Документація щодо відтворення ANR та рекомендації з усунення
- Підтримка після налаштування (2 тижні — безкоштовно)
Наш досвід: понад 5 років у Android-розробці, понад 50 проектів з моніторингом. Гарантуємо зниження ANR Rate нижче 0.2% після оптимізації.
Як швидко налаштувати ANR-моніторинг?
Базове налаштування з Firebase Crashlytics займає від 4 годин до 1 дня. Якщо потрібен Sentry та аналіз ApplicationExitInfo — 2 дні. Ми надаємо готовий дашборд та алерти. Зв'яжіться з нами — отримайте детальну оцінку вашого проекту.
Строки та вартість
Базове налаштування — від 4 годин до 1 дня. З аналізом ApplicationExitInfo та дашбордом — 2 дні. Вартість розраховується індивідуально залежно від складності проекту та кількості екранів. Інвестиції окупаються за рахунок зниження відтоку користувачів. Отримайте оцінку — напишіть нам.







