ANR (Application Not Responding) — системско обавештење Android-а које се појављује када апликација не реагује на унос током 5 секунди. За разлику од глукова (логичке грешке без блокирања UI) и лагова (успоравање без потпуног заустављања), ANR је критични квар који бележи оперативни систем: Android приказује дијалог „Апликација не реагује” са предлогом да је затворите или сачекате. Према Android Vitals Documentation, апликације са ANR стопом изнад 0,5% имају нижи рејтинг у Google Play-у и могу бити сакривене из препорука. Дијагностика укључује анализу /data/anr/traces.txt, коришћење StrictMode и профилисање главне нити.
Главно
ANR (Application Not Responding) — механизам заштите корисника у Android-у који се активира када апликација престане да реагује на унос. Систем прати време обраде догађаја: ако BroadcastReceiver не заврши onReceive у 10 секунди, Service се не врати из onCreate за 20 секунди, а ContentProvider не одговори за 15 секунди — Android генерише ANR.
Када настане ANR, Android приказује системски дијалог изнад свих прозора: „Апликација не реагује. Затворити је или сачекати?”. Корисник може да затвори апликацију или сачека њен опоравак. Ако се ANR често понавља, корисник брише апликацију. Google Play узима у обзир ANR стопу — проценат сесија са ANR-ом — у алгоритмима рангирања.
На iOS-у не постоји аналог ANR-а са системским дијалогом. Уместо тога, Apple користи Watchdog, који завршава процес апликације кодом 0x8badf00d. Корисник не види дијалог — апликација се једноставно затвара на главни екран. То чини ANR на Android-у видљивијим за корисника, али даје систему више информација за дијагностику.
ANR настаје када систем прати тајмаут за једну од четири врсте компоненти. Свака компонента има своје временско ограничење.
BroadcastReceiver се извршава у главној нити. Ако onReceive покрене синхрони мрежни захтев, дугу операцију уписа у базу или чека на блокаду — после 10 секунди настаје ANR. Решење: користите goAsync() и WorkManager за обраду у позадини. Типичан сценариј — примање Push обавештења од FCM-а и синхроно чување у Room-у.
Service.onCreate и Service.onStartCommand имају лимит од 20 секунди. Ако сервис покреће тешку иницијализацију (учитавање библиотека, читање конфигурације из мреже) у главној нити — ANR је неизбежан. Користите IntentService (застарео) или WorkManager за гарантовано извршење у позадинској нити.
ContentProvider.onCreate се извршава пре позива Application.onCreate и има лимит од 15 секунди. Ако провајдер врши миграцију базе, учитавање речника или иницијализацију SDK-а из мреже — то изазива ANR при покретању апликације. Решење: lazy-иницијализација, пребацивање тешких операција у WorkManager.
Android пружа неколико алата за анализу ANR-а: од системских дневника до специјализованих библиотека.
При сваком ANR-у, Android чува датотеку /data/anr/traces.txt са одливом стекова свих нити апликације. Пронађите нит „main” — последњи метод у стеку указује на узрок. Типични обрасци: Thread.sleep(), InputStream.read(), BinderProxy.transact(). За издвајање датотеке са уређаја користите adb са суперкорисничким правима.
Firebase Crashlytics аутоматски прикупља ANR и приказује их на контролној табли заједно са трасирањем. За Android 11+, ANR извештаји стижу са комплетним стеком главне нити. Интеграција захтева додавање зависности и иницијализацију FirebaseApp у Application.onCreate.
CPU Profiler у Android Studio-у омогућава снимање трасирања рада апликације и увид у то који методи заузимају процесорско време. Укључите „Record with method traces” и репродукујте сценариј који изазива ANR. На временској линији ће бити видљиво који су се методи извршавали у главној нити у тренутку замрзавања.
Пример интеграције Firebase Crashlytics-а за прикупљање ANR-а на Android-у:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(true)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
}
Отклањање ANR-а је пре свега пребацивање свих дугих операција из главне нити у позадинске. Размотримо конкретне технике за сваку врсту компоненте.
WorkManager — Google-ово препоручено решење за рад у позадини. Гарантује извршење задатка у позадинској нити узимајући у обзир стање уређаја. За разлику од Service-а, WorkManager не блокира главну нит и отпоран је на поновно покретање апликације. За BroadcastReceiver користите goAsync() и пренесите резултат PendingResult у WorkManager.
Покрећите све мрежне захтеве, рад са базом и операције са датотекама са Dispatchers.IO. Главна нит треба само да ажурира UI. Користите viewModelScope за аутоматско отказивање корутина при уништењу Activity-ја. Избегавајте runBlocking() у било ком контексту — то је синхроно блокирање тренутне нити.
Ако ContentProvider врши дугу иницијализацију, користите механизам одложеног учитавања: направите провајдер који враћа податке одмах, а тешку иницијализацију покрените кроз WorkManager са закашњењем. Ово спречава ANR при покретању апликације, када је систем најосетљивији на кашњења.
Пример правилног коришћења BroadcastReceiver-а са goAsync у Android-у:
class FcmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val pendingResult = goAsync()
WorkManager.getInstance(context!!)
.enqueue(OneTimeWorkRequest.from(NotificationWorker::class.java))
pendingResult.finish()
}
}
Најбољи начин борбе против ANR-а је спречавање њиховог појављивања у фази развоја кроз алате и архитектонска решења.
StrictMode са укљученим политикама detectNetwork() и detectDiskReads()/detectDiskWrites() открива потенцијалне ANR у фази развоја. У Debug верзији подесите penaltyDeath — свако кршење ће довести до тренутног пада, а програмер ће видети проблем пре комитовања.
Firebase Performance прати време извршења кључних операција и показује који сценарији прелазе праг ANR-а. Подесите прилагођена трасирања за сваки екран и мрежни захтев. Ако време извршења прелази 3 секунде — то је потенцијални ANR који захтева оптимизацију.
Симулирајте споре услове: ограничите брзину мреже преко Network Link Conditioner-а на iOS-у или Android Emulator-у. Успорите читање са диска кроз емулацију споре меморије. ANR се често појављује управо у таквим условима, на брзим уређајима програмера није видљив.
Често постављана питања
Android експлицитно прати време обраде догађаја на главној нити и приказује дијалог ANR. iOS користи Watchdog, који присилно затвара апликацију при замрзавању дужем од 10–20 секунди. ANR је карактеристика архитектуре Android-а, где неколико компоненти (BroadcastReceiver, Service) имају строге тајмауте.
На Android 11+ можете добити ANR дамп кроз adb shell dumpsys dropbox --print data_app_anr. На Android 10 и нижем без root-а нема приступа /data/anr/traces.txt. Користите Firebase Crashlytics — аутоматски прикупља ANR извештаје за Android 11+.
Google Play препоручује ANR стопу мању од 0,5% — односно не више од 5 ANR на 1000 сесија. Апликација са стопом изнад 1% добија упозорење у Google Play Console и може бити сакривена из препорука. Идеално, ANR стопа треба да буде испод 0,1%.
Корутина сама по себи не блокира нит. Али ако се унутар корутине извршава runBlocking на главној нити или је корутина покренута са Dispatchers.Main и извршава дугу CPU операцију — то ће изазвати ANR. Користите Dispatchers.IO за улаз-излаз и Dispatchers.Default за прорачуне.
Користите Android Emulator са профилом „Slow Network” или напишите тест који позива Thread.sleep(6000) на главној нити. Покрените апликацију кроз Debug и после 5 секунди ћете видети ANR дијалог. Проверите да се у logcat-у појавио запис о ANR-у са трасирањем.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође