Błędy ANR

Gdy wątek UI aplikacji na Androida jest zablokowany przez zbyt długi czas, wywoływany jest błąd „Aplikacja nie odpowiada” (ANR). Jeśli aplikacja działa na pierwszym planie, system wyświetla użytkownikowi okno, jak pokazano na ilustracji 1. W oknie błędu ANR użytkownik może wymusić zamknięcie aplikacji.

Okno ANR wyświetlane użytkownikowi.
Ilustracja 1. Okno błędu ANR wyświetlane użytkownikowi

Błędy ANR są problemem, ponieważ główny wątek aplikacji, który odpowiada za aktualizowanie interfejsu, nie może przetwarzać zdarzeń wejściowych użytkownika ani rysować, co powoduje frustrację użytkownika. Więcej informacji o głównym wątku aplikacji znajdziesz w artykule Omówienie procesów i wątków.

Błąd ANR jest wywoływany w aplikacji, gdy wystąpi jeden z tych warunków:

  • Przekroczenie limitu czasu wysyłania danych wejściowych: jeśli aplikacja nie odpowiedziała na zdarzenie wejściowe (np. naciśnięcie klawisza lub dotknięcie ekranu) w ciągu 5 sekund.
  • Wykonywanie usługi: jeśli usługa zadeklarowana przez aplikację nie może dokończyć wykonywania Service.onCreate i Service.onStartCommand/Service.onBind w ciągu kilku sekund.
  • Service.startForeground nie wywołano: jeśli aplikacja używa Context.startForegroundService do uruchomienia nowej usługi na pierwszym planie ale usługa nie wywoła startForeground w ciągu 5 sekund.
  • Rozgłaszanie intencji: jeśli BroadcastReceiver nie zakończył wykonywania w określonym czasie. Jeśli aplikacja ma jakąkolwiek aktywność na pierwszym planie, ten limit czasu wynosi 5 sekund.
  • JobScheduler interakcje: jeśli JobService nie zwróci wartości z JobService.onStartJob lub JobService.onStopJob w ciągu kilku sekund albo jeśli zadanie zainicjowane przez użytkownika zostanie uruchomione, a aplikacja nie wywoła JobService.setNotification w ciągu kilku sekund po wywołaniu JobService.onStartJob. W przypadku aplikacji kierowanych na Androida 13 i starsze wersje błędy ANR są ciche i nie są zgłaszane aplikacji. W przypadku aplikacji kierowanych na Androida 14 i nowsze wersje błędy ANR są jawne i zgłaszane aplikacji.

Jeśli w aplikacji występują błędy ANR, możesz skorzystać z informacji w tym dokumencie, aby zdiagnozować i rozwiązać problem.

Wykrywanie problemu

Jeśli aplikacja została już opublikowana, możesz użyć Android Vitals, aby wyświetlić informacje o błędach ANR w aplikacji. Do wykrywania błędów ANR w terenie możesz używać innych narzędzi, ale pamiętaj, że narzędzia innych firm nie mogą zgłaszać błędów ANR w Androidzie 10 i starszych wersjach, w przeciwieństwie do Android Vitals.

Android Vitals

Android Vitals może pomóc Ci monitorować i poprawiać częstotliwość błędów ANR w aplikacji. Android Vitals mierzy kilka częstotliwości błędów ANR:

  • Częstotliwość błędów ANR: odsetek liczby aktywnych użytkowników dziennie, u których wystąpił dowolny typ błędu ANR.
  • Częstotliwość błędów ANR widocznych dla użytkowników: odsetek liczby aktywnych użytkowników dziennie, u których wystąpił co najmniej 1 błąd ANR widoczny dla użytkowników. Obecnie za błędy ANR widoczne dla użytkowników uznawane są tylko błędy typu Input dispatching timed out.
  • Częstotliwość wielokrotnych błędów ANR: odsetek liczby aktywnych użytkowników dziennie, u których wystąpiły co najmniej 2 błędy ANR.

Aktywny użytkownik dziennie to unikalny użytkownik, który korzysta z aplikacji w ciągu 1 dnia na 1 urządzeniu, potencjalnie w ramach wielu sesji. Jeśli użytkownik korzysta z aplikacji na więcej niż 1 urządzeniu w ciągu 1 dnia, każde z tych urządzeń wpłynie na liczbę aktywnych użytkowników w danym dniu.

Częstotliwość błędów ANR widocznych dla użytkowników jest jednym z podstawowych wskaźników, co oznacza, że wpływa na możliwość odkrycia Twojej aplikacji w Google Play. Jest to ważne, ponieważ zliczane błędy ANR występują zawsze, gdy użytkownik korzysta z aplikacji, co powoduje najwięcej zakłóceń.

W przypadku tego wskaźnika w Google Play obowiązują 2 progi niewłaściwego działania:

  • Ogólny próg niewłaściwego działania: błąd ANR widoczny dla użytkowników wystąpił u co najmniej 0, 47% liczby aktywnych użytkowników dziennie na wszystkich modelach urządzeń.
  • Próg niewłaściwego działania na poziomie urządzenia: błąd ANR widoczny dla użytkowników wystąpił u co najmniej 8% użytkowników dziennie na 1 modelu urządzenia.

Jeśli Twoja aplikacja przekracza ogólny próg niewłaściwego działania, prawdopodobnie będzie trudniejsza do odkrycia na wszystkich urządzeniach. Jeśli Twoja aplikacja przekracza próg niewłaściwego działania na poziomie urządzenia na niektórych urządzeniach, prawdopodobnie będzie trudniejsza do odkrycia na tych urządzeniach, a na jej stronie w Sklepie może wyświetlać się ostrzeżenie.

Android Vitals może wysyłać alerty w Konsoli Play, gdy w aplikacji wystąpi zbyt wiele błędów ANR.

Więcej informacji o tym, jak Google Play zbiera dane Android Vitals, znajdziesz w dokumentacji Play Console.

Diagnozowanie błędów ANR

Podczas diagnozowania błędów ANR warto zwrócić uwagę na te typowe wzorce:

  • Aplikacja wykonuje powolne operacje wejścia-wyjścia w wątku głównym.
  • Aplikacja wykonuje długie obliczenia w wątku głównym.
  • Wątek główny wykonuje synchroniczne wywołanie bindera do innego procesu, a ten proces długo czeka na powrót.
  • Wątek główny jest zablokowany i czeka na zsynchronizowany blok długiej operacji, która jest wykonywana w innym wątku.
  • Wątek główny jest w stanie zakleszczenia z innym wątkiem, w Twoim procesie lub przez wywołanie bindera. Wątek główny nie tylko czeka na zakończenie długiej operacji, ale też jest w stanie zakleszczenia.

Poniższe techniki mogą pomóc Ci określić przyczynę błędów ANR.

HealthStats

HealthStats udostępnia dane o stanie aplikacji, rejestrując łączny czas użytkownika i systemu, czas procesora, statystyki sieci i radia, czas włączenia i wyłączenia ekranu oraz alarmy wybudzania. Może to pomóc w pomiarze ogólnego wykorzystania procesora i zużycia baterii.

Debugowanie

Debug pomaga w sprawdzaniu aplikacji na Androida podczas programowania, w tym w śledzeniu i zliczaniu alokacji, aby wykryć zacięcia i opóźnienia w aplikacjach. Możesz też użyć Debug, aby uzyskać liczniki pamięci natywnej i pamięci w czasie wykonywania oraz dane o pamięci, które mogą pomóc w określeniu wykorzystania pamięci przez dany proces.

ApplicationExitInfo

ApplicationExitInfo jest dostępny w Androidzie 11 (poziom API 30) lub nowszym i zawiera informacje o przyczynie zamknięcia aplikacji. Obejmuje to błędy ANR, brak pamięci, awarie aplikacji, nadmierne wykorzystanie procesora, przerwy spowodowane przez użytkownika, przerwy systemowe i zmiany uprawnień w czasie wykonywania.

Tryb ścisły

Używanie StrictMode pomaga znaleźć przypadkowe operacje wejścia-wyjścia w wątku głównym podczas programowania aplikacji. Możesz używać StrictMode na poziomie aplikacji lub aktywności.

Włączanie okien błędu ANR w tle

Android wyświetla okna błędu ANR w przypadku aplikacji, które zbyt długo przetwarzają komunikat rozgłaszany, tylko wtedy, gdy w opcjach programisty na urządzeniu jest włączona opcja Pokaż wszystkie błędy ANR. Z tego powodu okna błędu ANR w tle nie zawsze są wyświetlane użytkownikowi, nawet jeśli w aplikacji występują problemy z wydajnością.

Wąskie gardła rekompozycji

Użyj profilera Android Studio i narzędzia Layout Inspector, aby wykryć wąskie gardła rekompozycji. Więcej informacji znajdziesz w artykule Jetpack Compose Performance.

Pobieranie pliku śledzenia

Gdy Android napotka błąd ANR, zapisuje informacje o śledzeniu. W starszych wersjach systemu operacyjnego na urządzeniu znajduje się jeden plik /data/anr/traces.txt. W nowszych wersjach systemu operacyjnego jest kilka plików /data/anr/anr_*. Dostęp do śladów błędów ANR z urządzenia lub emulatora można uzyskać za pomocą narzędzia Android Debug Bridge (adb) jako root:

adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>

Raport o błędzie możesz utworzyć na urządzeniu fizycznym, korzystając z opcji programisty Zgłoś błąd na urządzeniu lub polecenia adb bugreport na komputerze używanym do programowania. Więcej informacji znajdziesz w artykule Tworzenie i odczytywanie raportów o błędach.

Rozwiązywanie problemów

Po zidentyfikowaniu problemu możesz skorzystać ze wskazówek w tej sekcji, aby rozwiązać często spotykane problemy.

Powolny kod w wątku głównym

Określ miejsca w kodzie, w których wątek główny aplikacji jest zajęty przez ponad 5 sekund. Poszukaj podejrzanych przypadków użycia w aplikacji i spróbuj odtworzyć błąd ANR.

Częstym problemem jest długotrwałe zadanie bezpośrednio w komponencie:

@Composable
fun BadList(rawStrings: List<String>) {
    // Math or sorting inside the composable runs on EVERY recomposition pass!
    val heavilyProcessedList = rawStrings
        .filter { it.isNotBlank() }
        .map { it.uppercase().reversed() }
        .map { it.computationallyHeavyFunction() }
.sortedBy { it.length }
    LazyColumn { items(sortedList) { Text(it) } }
}

// Modern Compose-first fix
@Composable
fun GoodList(viewModel: MyViewModel = viewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()

    // UI simply renders state; no heavy processing allowed here
    LazyColumn { items(uiState.sortedData) { Text(it) } }
}

Wejście-wyjście w wątku głównym

Wykonywanie operacji wejścia-wyjścia w wątku głównym jest częstą przyczyną powolnych operacji w wątku głównym, co może powodować błędy ANR. W Compose programiści często przypadkowo wywołują odczyty z dysku (np. SharedPreferences lub wywołania bazy danych) podczas próby uzyskania stanu początkowego.

Długotrwałe operacje wejścia-wyjścia wykonuj poza warstwą interfejsu. Użyj withContext(Dispatchers.IO) w ViewModel lub, co jeszcze lepsze, użyj a Repository na warstwie danych.

Zakleszczenia

Zakleszczenie występuje, gdy wątek przechodzi w stan oczekiwania, ponieważ wymagany zasób jest używany przez inny wątek, który również czeka na zasób używany przez pierwszy wątek. Jeśli wątek główny aplikacji znajduje się w takiej sytuacji, prawdopodobnie wystąpią błędy ANR.

Zakleszczenia są dobrze zbadanym zjawiskiem w informatyce. Istnieją algorytmy zapobiegające zakleszczeniom, których możesz użyć, aby ich uniknąć.

Więcej informacji znajdziesz w artykułach Deadlock i Deadlock prevention algorithms w Wikipedii.

Gdy używasz Kotlina i Compose, możesz zastąpić pierwotne blokady nieblokującymi muteksami współprogramów (Mutex.withLock), aby zapobiec blokowaniu wątków przez zawieszenie kontekstu wykonywania zamiast zamrażania wątku UI. Przykład:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// Modern non-blocking concurrency state architecture
class SecureDataRepository {
    private val mutex = Mutex()

    suspend fun safeUIAccess() {
        // If locked, the main thread suspends seamlessly, preventing an ANR
        mutex.withLock {
            performSafeOperation()
        }
    }
}

Powolne odbiorniki

Aplikacje mogą odpowiadać na komunikaty rozgłaszane, np. włączanie lub wyłączanie trybu samolotowego albo zmianę stanu połączenia, za pomocą odbiorników. Błąd ANR występuje, gdy aplikacja zbyt długo przetwarza komunikat rozgłaszany.

Błąd ANR występuje w tych przypadkach:

Aplikacja powinna wykonywać tylko krótkie operacje w metodzie onReceive odbiornika BroadcastReceiver. Jeśli jednak aplikacja wymaga bardziej złożonego przetwarzania w wyniku komunikatu rozgłaszanego, powinna przekazać zadanie do ViewModel (wykorzystując możliwości współprogramów, zakresów i dyspozytorów Kotlina) jeśli zadanie ma trwać co najwyżej kilka sekund, do dowolnego typu zmiennej stanu, lub do WorkManager w przypadku zadań, które mają trwać dłużej niż kilka sekund.

GameActivity

Biblioteka GameActivity zmniejszyła liczbę błędów ANR w studiach przypadków gier i aplikacji napisanych w C lub C++. Jeśli zastąpisz dotychczasową aktywność natywną GameActivity, możesz zmniejszyć blokowanie wątku UI i zapobiec niektórym błędom ANR.

Więcej informacji o błędach ANR znajdziesz w artykule Zapewnianie responsywności aplikacji. Więcej informacji o wątkach znajdziesz w artykule Lepsza wydajność dzięki wielowątkowości.

Dodatkowe materiały

Wyświetlanie treści