Gdy wątek UI aplikacji na Androida jest zablokowany zbyt długo, pojawia się błąd „Aplikacja nie odpowiada” (ANR). Jeśli aplikacja jest na pierwszym planie, system wyświetla użytkownikowi okno dialogowe, jak pokazano na rysunku 1. Okno błędu ANR daje użytkownikowi możliwość wymuszenia 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 frustruje 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:
- Przekazywanie danych wejściowych przekroczyło limit czasu:jeśli aplikacja nie odpowie 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
Service.onCreateiService.onStartCommand/Service.onBindw ciągu kilku sekund. Service.startForegroundnie wywołano: jeśli aplikacja używa funkcjiContext.startForegroundServicedo uruchomienia nowej usługi na pierwszym planie, ale usługa nie wywołuje funkcjistartForegroundw ciągu 5 sekund.- Nadawanie intencji: jeśli
BroadcastReceivernie zakończył wykonywania w ustalonym czasie. Jeśli aplikacja jest aktywna na pierwszym planie, ten czas oczekiwania wynosi 5 sekund. JobSchedulerinterakcje: jeśliJobServicenie wraca zJobService.onStartJoblubJobService.onStopJobw ciągu kilku sekund lub 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 i starszego błędy ANR są ciche i nie są zgłaszane do aplikacji. W przypadku aplikacji kierowanych na Androida 14 i nowszego błędy ANR są jawne i są zgłaszane do aplikacji.
Jeśli w aplikacji występują błędy ANR, możesz skorzystać z informacji 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 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 Binder do innego procesu, a ten proces długo zwraca wynik.
- Wątek główny jest zablokowany i oczekuje na zsynchronizowany blok długotrwałej operacji, która jest wykonywana w innym wątku.
- Wątek główny znajduje się w zakleszczeniu z innym wątkiem w Twoim procesie lub w wywołaniu bindera. Wątek główny nie tylko czeka na zakończenie długotrwałej operacji, ale znajduje się w sytuacji zakleszczenia.
Poniższe techniki mogą pomóc w określeniu przyczyny 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/wyłączenia ekranu oraz alarmy budzenia. Pomoże Ci to mierzyć ogólne wykorzystanie procesora i zużycie baterii.
Debugowanie
Debug pomaga sprawdzać aplikacje na Androida podczas ich tworzenia, w tym śledzić i liczyć alokacje, aby wykrywać w nich zacięcia i opóźnienia.
Możesz też użyć Debug, aby uzyskać liczniki pamięci natywnej i czasu działania oraz dane dotyczące pamięci, które pomogą Ci określić wykorzystanie pamięci konkretnego procesu.
ApplicationExitInfo
ApplicationExitInfo jest dostępny na Androidzie 11 (poziom interfejsu API 30) lub nowszym i zawiera informacje o przyczynie zamknięcia aplikacji. Obejmuje to błędy ANR, niski poziom 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.
Tryb ścisły
Używanie StrictMode pomaga podczas tworzenia aplikacji znajdować przypadkowe operacje wejścia/wyjścia w głównym wątku. Możesz używać StrictMode na poziomie aplikacji lub aktywności.
Włączanie okien ANR w tle
Android wyświetla okna ANR w przypadku aplikacji, które zbyt długo przetwarzają komunikat transmisji, tylko wtedy, gdy w opcjach programisty na urządzeniu włączona jest opcja Pokaż wszystkie błędy ANR. Z tego powodu okna dialogowe 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 Androida Studio i narzędzia Layout Inspector, aby śledzić wąskie gardła rekompozycji. Więcej informacji znajdziesz w artykule Jetpack Compose Performance (w języku angielskim).
Pobieranie pliku śladów
Gdy w systemie Android wystąpi błąd ANR, zapisuje on informacje o śledzeniu. W starszych wersjach systemu operacyjnego na urządzeniu znajduje się jeden plik /data/anr/traces.txt. W nowszych wersjach systemu operacyjnego występuje kilka plików /data/anr/anr_*. Do śladów ANR na urządzeniu lub emulatorze możesz uzyskać dostęp za pomocą 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 utworzyć na urządzeniu fizycznym, korzystając z opcji programisty Zgłoś błąd na urządzeniu lub z 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 wątku głównym
Zidentyfikuj miejsca w kodzie, w których główny wątek aplikacji jest zajęty przez ponad 5 sekund. Wyszukaj w aplikacji podejrzane przypadki 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ą powolnych operacji w wątku głównym, które mogą powodować błędy ANR. W Compose deweloperzy często przypadkowo wywołują odczyty z dysku (np. SharedPreferences lub wywołania bazy danych) podczas próby uzyskania stanu początkowego.
Wykonywanie długotrwałych operacji wejścia/wyjścia poza warstwą interfejsu. Użyj
withContext(Dispatchers.IO) w ViewModel lub, co jeszcze lepsze, użyj
Repository w 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ż oczekuje na zasób używany przez pierwszy wątek. Jeśli główny wątek aplikacji jest 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żywać, aby ich uniknąć.
Więcej informacji znajdziesz w artykułach Zakleszczenie i Algorytmy zapobiegające zakleszczeniom w Wikipedii.
Jeśli używasz języka Kotlin i biblioteki Compose, możesz zastąpić blokady pierwotne nieblokującymi wątków muteksami (Mutex.withLock) współprogramów, aby zapobiec blokowaniu wątków przez zawieszanie kontekstu wykonania 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 wiadomości rozgłoszeniowe, np. włączanie lub wyłączanie trybu samolotowego czy zmianę stanu połączenia, za pomocą odbiorników rozgłoszeniowych. Błąd ANR występuje, gdy aplikacja zbyt długo przetwarza komunikat.
Błąd ANR występuje w tych przypadkach:
- Odbiornik transmisji nie zakończył wykonywania metody
onReceivew rozsądnym czasie. - Odbiornik transmisji wywołuje metodę
goAsynci nie może wywołać metodyfinishna 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 rozgłoszeniowego, odłóż to zadanie na później, korzystając z ViewModel (wykorzystując moc współprogramów, zakresów i dispatcherów w Kotlinie), jeśli zadanie ma trwać co najwyżej kilka sekund, dowolnego rodzaju zmiennej stanu lub 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 istniejącą działanie natywne biblioteką GameActivity, możesz zmniejszyć blokowanie wątku UI i zapobiec występowaniu niektórych błędów 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 wątkom.
Dodatkowe materiały
Wyświetla treści
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Zbyt częste wybudzenia