Gdy wątek UI aplikacji na Androida jest zablokowany zbyt długo, pojawia się błąd „Aplikacja nie odpowiada” (ANR). Jeśli aplikacja działa na pierwszym planie, system wyświetla użytkownikowi okno, jak na rysunku 1. Okno ANR umożliwia użytkownikowi wymuszenie zamknięcia aplikacji.
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 omówieniu 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 zareagował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 zakończyć wykonywania metod
Service.onCreateiService.onStartCommand/Service.onBindw ciągu kilku sekund. - Nie wywołano
Service.startForeground: jeśli aplikacja używaContext.startForegroundServicedo uruchomienia nowej usługi na pierwszym planie, ale usługa nie wywołujestartForegroundw ciągu 5 sekund. - Rozgłaszanie intencji: jeśli
BroadcastReceivernie zakończy się w określonym czasie. Jeśli aplikacja jest aktywna na pierwszym planie, limit czasu wynosi 10 sekund. - Interakcje z
JobScheduler: jeśliJobServicenie zwróci odpowiedzi zJobService.onStartJoblubJobService.onStopJobw ciągu kilku sekund albo jeśli rozpocznie się zadanie zainicjowane przez użytkownika, a aplikacja nie wywołaJobService.setNotificationw ciągu kilku sekund po wywołaniuJobService.onStartJob. W przypadku aplikacji kierowanych na Androida 13 lub starszego błędy ANR są ciche i nie są zgłaszane aplikacji. W przypadku aplikacji kierowanych na Androida 14 lub nowszego błędy ANR są jawne i są zgłaszane aplikacji.
Jeśli w aplikacji występują błędy ANR, możesz skorzystać z informacji zawartych w tym dokumencie, aby zdiagnozować i rozwiązać problem.
Diagnozowanie błędów ANR
Podczas diagnozowania błędów ANR warto zwrócić uwagę na kilka typowych wzorców:
- Aplikacja wykonuje powolne operacje wejścia-wyjścia w wątku głównym.
- Aplikacja wykonuje długie obliczenia w głównym wątku.
- Wątek główny wykonuje synchroniczne wywołanie Binder do innego procesu, a ten proces długo nie zwraca odpowiedzi.
- Wątek główny jest zablokowany i czeka na zsynchronizowany blok długiej operacji, która odbywa się w innym wątku.
- Wątek główny jest zablokowany przez inny wątek w Twoim procesie lub przez wywołanie binder. Wątek główny nie tylko czeka na zakończenie długiej operacji, ale jest w sytuacji zakleszczenia.
Poniższe techniki mogą pomóc Ci określić przyczynę błędów ANR.
HealthStats
HealthStats dostarcza dane o stanie aplikacji, rejestrując łączny czas użytkownika i systemu, czas pracy procesora, sieć, statystyki radiowe, czas włączenia/wyłączenia ekranu i alarmy wybudzenia. Może to pomóc w pomiarze ogólnego wykorzystania procesora i zużycia baterii.
Debugowanie
Debug pomaga sprawdzać aplikacje na Androida podczas ich tworzenia, m.in. śledzić i liczyć alokacje, aby wykrywać zacinanie się i opóźnienia w aplikacjach.
Możesz też użyć Debug, aby uzyskać liczniki pamięci środowiska wykonawczego i pamięci natywnej oraz wskaźniki pamięci, które pomogą Ci określić wykorzystanie pamięci przez dany proces.
ApplicationExitInfo
Interfejs ApplicationExitInfo jest dostępny na Androidzie 11 (poziom API 30) lub nowszym i zawiera informacje o przyczynie zamknięcia aplikacji. Obejmuje to błędy ANR, małą ilość pamięci, awarie aplikacji, nadmierne wykorzystanie procesora, przerwy w działaniu aplikacji spowodowane przez użytkownika, przerwy w działaniu aplikacji spowodowane przez system i zmiany uprawnień w czasie działania aplikacji.
Tryb ścisły
Korzystanie z StrictMode pomaga znaleźć przypadkowe operacje wejścia/wyjścia w głównym wątku podczas tworzenia aplikacji. Możesz używać StrictMode na poziomie aplikacji lub aktywności.
Włącz okna błędów ANR w tle
Android wyświetla okna ANR w przypadku aplikacji, które zbyt długo przetwarzają komunikat, tylko wtedy, gdy w Opcjach programisty na urządzeniu włączona jest opcja Pokaż wszystkie błędy ANR. Z tego powodu okna błędów ANR w tle nie zawsze są wyświetlane użytkownikowi, nawet jeśli aplikacja ma problemy z wydajnością.
Wąskie gardła rekompozycji
Użyj Profilera w Android Studio i Layout Inspectora, aby znaleźć wąskie gardła rekompozycji. Więcej informacji znajdziesz w artykule o wydajności Jetpack Compose.
Pobieranie pliku logów czasu
Android przechowuje informacje o śladach, gdy wystąpi błąd ANR. 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_*. Ślady błędów ANR możesz uzyskać z urządzenia lub emulatora, używając Android Debug Bridge (adb) jako użytkownik root:
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
Raport o błędzie możesz wygenerować 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 Przechwytywanie 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ć najczęstsze problemy.
Powolny kod w głównym wątku
Zidentyfikuj miejsca w kodzie, w których główny wątek aplikacji jest zajęty przez ponad 5 sekund. Poszukaj w aplikacji podejrzanych przypadków użycia i spróbuj odtworzyć błąd ANR.
Częstym problemem jest długotrwałe zadanie bezpośrednio w funkcji kompozycyjnej:
@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ą powolnego działania
wątku głównego, co może powodować błędy ANR. W Compose deweloperzy często przypadkowo wywołują odczyty dysku (np. SharedPreferences lub wywołania bazy danych) podczas próby uzyskania stanu początkowego.
Długo trwające operacje wejścia-wyjścia wykonuj poza warstwą interfejsu. Użyj withContext(Dispatchers.IO) w ViewModel lub, jeszcze lepiej, użyj Repository w warstwie danych.
Zakleszczenia
Zakleszczenie występuje, gdy wątek przechodzi w stan oczekiwania, ponieważ wymagany zasób jest zajęty przez inny wątek, który również oczekuje na zasób zajęty przez pierwszy wątek. Jeśli główny wątek aplikacji znajduje się w takiej sytuacji, prawdopodobnie wystąpią błędy ANR.
Zakleszczenia to dobrze znane zjawisko w informatyce. Istnieją algorytmy zapobiegania zakleszczeniom, których można używać, aby ich unikać.
Więcej informacji znajdziesz w artykułach Zakleszczenie i Algorytmy zapobiegania zakleszczeniom w Wikipedii.
Jeśli używasz Kotlina i Compose, możesz zastąpić blokady podstawowe nieblokującymi muteksami współprogramów (Mutex.withLock), aby zapobiec blokowaniu wątków przez zawieszenie kontekstu wykonywania zamiast zamrażania wątku interfejsu. Na 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ą reagować na komunikaty, takie jak włączenie lub wyłączenie trybu samolotowego albo zmiana stanu połączenia, za pomocą odbiorników komunikatów. Błąd ANR występuje, gdy aplikacja zbyt długo przetwarza komunikat.
Błąd ANR występuje w tych przypadkach:
- Odbiornik komunikatów nie zakończył wykonywania metody
onReceivew odpowiednim czasie. - Odbiornik wywołuje metodę
goAsync, ale nie wywołuje metodyfinishw obiekciePendingResult.
Aplikacja powinna wykonywać tylko krótkie operacje w metodzie onReceive klasy BroadcastReceiver. Jeśli jednak aplikacja wymaga bardziej złożonego przetwarzania w wyniku otrzymania komunikatu, należy odroczyć zadanie do ViewModel (wykorzystując możliwości współprogramów, zakresów i dyspozytorów w Kotlin), jeśli zadanie ma potrwać co najwyżej kilka sekund, dowolnego typu zmiennej stanu lub do WorkManager w przypadku zadań, które mają potrwać 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 języku C lub C++. Jeśli zastąpisz dotychczasowe działanie natywne biblioteką GameActivity, możesz ograniczyć blokowanie wątku UI i zapobiec niektórym błędom ANR.
Więcej informacji o błędach ANR znajdziesz w artykule Zapewnianie szybkiego działania aplikacji. Więcej informacji o wątkach znajdziesz w artykule Lepsza wydajność dzięki wątkom.
Dodatkowe materiały
Wyświetlanie treści
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Zbyt częste wybudzenia