ANR (Application Not Responding) — to systemowe powiadomienie Androida, które pojawia się, gdy aplikacja nie reaguje na wprowadzanie danych przez 5 sekund. W przeciwieństwie do glitchy (błędy logiczne bez blokowania UI) i lagów (spowolnienie bez całkowitego zatrzymania), ANR to krytyczna awaria rejestrowana przez system operacyjny: Android wyświetla okno dialogowe „Aplikacja nie odpowiada” z propozycją zamknięcia lub oczekiwania. Według Android Vitals Documentation, aplikacje ze wskaźnikiem ANR powyżej 0,5% mają obniżoną pozycję w Google Play i mogą być ukryte przed rekomendacjami. Diagnostyka obejmuje analizę /data/anr/traces.txt, użycie StrictMode i profilowanie głównego wątku.
Najważniejsze
ANR (Application Not Responding) — to mechanizm ochrony użytkownika w Androidzie, który uruchamia się, gdy aplikacja przestaje reagować na wprowadzanie danych. System śledzi czas przetwarzania zdarzeń: jeśli BroadcastReceiver nie kończy onReceive w ciągu 10 sekund, Service nie zwraca się z onCreate w ciągu 20 sekund, a ContentProvider nie odpowiada w ciągu 15 sekund — Android generuje ANR.
Kiedy występuje ANR, Android wyświetla systemowe okno dialogowe nad wszystkimi oknami: „Aplikacja nie odpowiada. Zamknąć ją czy poczekać?”. Użytkownik może zamknąć aplikację lub poczekać na jej odzyskanie. Jeśli ANR powtarzają się często, użytkownik usuwa aplikację. Google Play uwzględnia wskaźnik ANR — procent sesji z ANR — w algorytmach rankingu.
Na iOS nie ma odpowiednika ANR z systemowym oknem dialogowym. Zamiast tego Apple używa Watchdog, który kończy proces aplikacji kodem 0x8badf00d. Użytkownik nie widzi okna dialogowego — aplikacja po prostu zamyka się do ekranu głównego. To sprawia, że ANR na Androidzie jest bardziej zauważalny dla użytkownika, ale daje systemowi więcej informacji do diagnostyki.
ANR występuje, gdy system śledzi limit czasu dla jednego z czterech typów komponentów. Każdy komponent ma swój limit czasu.
BroadcastReceiver wykonuje się w głównym wątku. Jeśli onReceive uruchamia synchroniczne żądanie sieciowe, długą operację zapisu do bazy danych lub oczekuje na blokadę — po 10 sekundach pojawia się ANR. Rozwiązanie: używaj goAsync() i WorkManager do przetwarzania w tle. Typowy scenariusz — otrzymanie powiadomienia Push z FCM i synchroniczne zapisywanie w Room.
Service.onCreate i Service.onStartCommand mają limit 20 sekund. Jeśli serwis uruchamia ciężką inicjalizację (ładowanie bibliotek, odczyt konfiguracji z sieci) w głównym wątku — ANR jest nieunikniony. Używaj IntentService (przestarzały) lub WorkManager do gwarantowanego wykonania w wątku tła.
ContentProvider.onCreate wykonuje się przed wywołaniem Application.onCreate i ma limit 15 sekund. Jeśli provider wykonuje migrację bazy danych, ładowanie słowników lub inicjalizację SDK z sieci — powoduje to ANR przy uruchamianiu aplikacji. Rozwiązanie: lazy-inicjalizacja, przeniesienie ciężkich operacji do WorkManager.
Android udostępnia kilka narzędzi do analizy ANR: od logów systemowych po specjalistyczne biblioteki.
Przy każdym ANR Android zapisuje plik /data/anr/traces.txt z zrzutem stosów wszystkich wątków aplikacji. Znajdź wątek „main” — ostatnia metoda w stosie wskazuje przyczynę. Typowe wzorce: Thread.sleep(), InputStream.read(), BinderProxy.transact(). Do wyodrębnienia pliku z urządzenia użyj adb z uprawnieniami superużytkownika.
Firebase Crashlytics automatycznie zbiera ANR i wyświetla je na pulpicie wraz ze śledzeniem. Dla Android 11+ raporty ANR przychodzą z pełnym stosem głównego wątku. Integracja wymaga dodania zależności i inicjalizacji FirebaseApp w Application.onCreate.
CPU Profiler w Android Studio pozwala zapisać śledzenie działania aplikacji i zobaczyć, które metody zajmują czas procesora. Włącz „Record with method traces” i odtwórz scenariusz wywołujący ANR. Na osi czasu będzie widoczne, które metody wykonywały się w głównym wątku w momencie zawieszenia.
Przykład integracji Firebase Crashlytics do zbierania ANR na Androidzie:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(true)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
}
Usuwanie ANR to przede wszystkim przeniesienie wszystkich długich operacji z głównego wątku do tła. Rozważmy konkretne techniki dla każdego typu komponentu.
WorkManager — rekomendowane przez Google rozwiązanie do pracy w tle. Gwarantuje wykonanie zadania w wątku tła z uwzględnieniem stanu urządzenia. W przeciwieństwie do Service, WorkManager nie blokuje głównego wątku i jest odporny na ponowne uruchomienie aplikacji. Dla BroadcastReceiver użyj goAsync() i przekaż wynik PendingResult do WorkManager.
Uruchamiaj wszystkie żądania sieciowe, pracę z bazą danych i operacje plikowe z Dispatchers.IO. Główny wątek powinien tylko aktualizować UI. Używaj viewModelScope do automatycznego anulowania coroutines przy zniszczeniu Activity. Unikaj runBlocking() w jakimkolwiek kontekście — to synchroniczna blokada bieżącego wątku.
Jeśli ContentProvider wykonuje długą inicjalizację, użyj mechanizmu opóźnionego ładowania: stwórz provider, który zwraca dane natychmiast, a ciężką inicjalizację uruchom przez WorkManager z opóźnieniem. Zapobiega to ANR przy uruchamianiu aplikacji, gdy system jest najbardziej wrażliwy na opóźnienia.
Przykład prawidłowego użycia BroadcastReceiver z goAsync w 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()
}
}
Najlepszym sposobem walki z ANR jest zapobieganie ich pojawianiu się na etapie rozwoju poprzez narzędzia i rozwiązania architektoniczne.
StrictMode z włączonymi politykami detectNetwork() i detectDiskReads()/detectDiskWrites() wykrywa potencjalne ANR na etapie rozwoju. W kompilacji Debug ustaw penaltyDeath — każde naruszenie doprowadzi do natychmiastowego upadku, a programista zobaczy problem przed zatwierdzeniem.
Firebase Performance śledzi czas wykonania kluczowych operacji i pokazuje, które scenariusze przekraczają próg ANR. Skonfiguruj niestandardowe śledzenie dla każdego ekranu i żądania sieciowego. Jeśli czas wykonania przekracza 3 sekundy — to potencjalny ANR wymagający optymalizacji.
Symuluj wolne warunki: ogranicz prędkość sieci przez Network Link Conditioner na iOS lub Android Emulator. Spowolnij odczyt z dysku przez emulację wolnej pamięci. ANR często pojawiają się właśnie w takich warunkach, na szybkich urządzeniach programisty nie są widoczne.
Często zadawane pytania
Android jawnie śledzi czas przetwarzania zdarzeń na głównym wątku i wyświetla okno dialogowe ANR. iOS używa Watchdog, który wymusza zamknięcie aplikacji po zawieszeniu na ponad 10–20 sekund. ANR to cecha architektury Androida, gdzie kilka komponentów (BroadcastReceiver, Service) ma sztywne limity czasu.
Na Android 11+ można uzyskać zrzut ANR przez adb shell dumpsys dropbox --print data_app_anr. Na Android 10 i niżej bez root dostępu do /data/anr/traces.txt nie ma. Użyj Firebase Crashlytics — zbiera raporty ANR automatycznie dla Android 11+.
Google Play zaleca wskaźnik ANR poniżej 0,5% — czyli nie więcej niż 5 ANR na 1000 sesji. Aplikacja ze wskaźnikiem powyżej 1% otrzymuje ostrzeżenie w Google Play Console i może być ukryta przed rekomendacjami. Idealnie wskaźnik ANR powinien być poniżej 0,1%.
Coroutine sama w sobie nie blokuje wątku. Ale jeśli wewnątrz coroutine wykonywany jest runBlocking na głównym wątku lub coroutine jest uruchomiona z Dispatchers.Main i wykonuje długą operację CPU — to spowoduje ANR. Używaj Dispatchers.IO dla wejścia-wyjścia i Dispatchers.Default dla obliczeń.
Użyj Android Emulator z profilem „Slow Network” lub napisz test, który wywołuje Thread.sleep(6000) na głównym wątku. Uruchom aplikację przez Debug i po 5 sekundach zobaczysz okno dialogowe ANR. Sprawdź, czy w logcat pojawił się wpis o ANR ze śledzeniem.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również