Gdy wątek UI aplikacji na Androida jest zablokowany zbyt długo, system wysyła błąd „Aplikacja nie odpowiada” (ANR). Na tej stronie opisujemy różne typy błędów ANR, sposoby ich diagnozowania i sugestie dotyczące ich naprawy. Wszystkie wymienione domyślne zakresy czasu oczekiwania dotyczą urządzeń z AOSP i Pixeli. Mogą się one różnić w zależności od producenta OEM.
Pamiętaj, że przy określaniu przyczyny błędów ANR warto rozróżniać problemy z systemem i aplikacją.
Gdy system jest w złym stanie, błędy ANR mogą być spowodowane tymi problemami:
- Przejściowe problemy z serwerem systemu powodują, że zwykle szybkie wywołania interfejsu Binder stają się wolne.
- Problemy z serwerem systemowym i duże obciążenie urządzenia powodują, że wątki aplikacji nie są planowane.
Jeśli masz taką możliwość, dobrym sposobem na odróżnienie problemów z systemem od problemów z aplikacją jest użycie śladów Perfetto:
- Sprawdź, czy wątek główny aplikacji jest zaplanowany, przeglądając stan wątku w Perfetto, aby zobaczyć, czy jest on uruchomiony lub czy można go uruchomić.
- Sprawdź wątki
system_serverpod kątem problemów, takich jak rywalizacja o blokadę. - W przypadku wolnych wywołań interfejsu Binder sprawdź wątek odpowiedzi, jeśli jest dostępny, aby dowiedzieć się, dlaczego są one wolne.
Czas oczekiwania na wysłanie danych wejściowych
Błędy ANR związane z przekazywaniem danych wejściowych występują, gdy główny wątek aplikacji nie odpowiada na zdarzenie wejściowe, takie jak przesunięcie lub naciśnięcie klawisza, w odpowiednim czasie. Ponieważ aplikacja jest na pierwszym planie, gdy występują przekroczenia limitu czasu wysyłania danych wejściowych, są one prawie zawsze widoczne dla użytkownika i bardzo ważne do wyeliminowania.
Domyślny okres oczekiwania: 5 sekund.
Błędy ANR związane z przekazywaniem danych wejściowych są zwykle spowodowane problemami w wątku głównym. Jeśli wątek główny został zablokowany podczas oczekiwania na uzyskanie blokady, może to również dotyczyć wątku, który ją stosuje.
Aby uniknąć błędów ANR związanych z przekazywaniem danych wejściowych, postępuj zgodnie z tymi sprawdzonymi metodami:
- Nie wykonuj w wątku głównym operacji blokujących ani długotrwałych. Rozważ użycie
StrictMode, aby wykrywać przypadkową aktywność w wątku głównym. - Zminimalizuj rywalizację o blokadę między wątkiem głównym a innymi wątkami.
- Minimalizuj pracę niezwiązaną z interfejsem w wątku głównym, np. podczas obsługi transmisji lub uruchamiania usług.
Typowe przyczyny
Oto kilka typowych przyczyn błędów ANR związanych z przekazywaniem danych wejściowych i sugerowane rozwiązania.
| Przyczyna | Co się dzieje | Sugerowane poprawki |
|---|---|---|
| Powolne wywołanie Binder | Wątek główny wykonuje długie synchroniczne wywołanie Binder. | Jeśli jesteś właścicielem interfejsu API, przenieś wywołanie poza główny wątek lub spróbuj je zoptymalizować. |
| Wiele kolejnych wywołań interfejsu Binder | Wątek główny wykonuje wiele kolejnych synchronicznych wywołań Binder. | Nie wykonuj wywołań interfejsu Binder w ciasnej pętli. |
| Blokowanie wejścia/wyjścia | Wątek główny wykonuje blokujące wywołanie wejścia/wyjścia, np. dostęp do bazy danych lub sieci. | Przenieś wszystkie blokujące operacje wejścia/wyjścia z wątku głównego. |
| Rywalizacja o blokadę | Wątek główny jest zablokowany i oczekuje na uzyskanie blokady. | Zmniejsz rywalizację o blokadę między wątkiem głównym a innymi wątkami. Optymalizuj powolny kod w innym wątku. |
| Droga ramka | Renderowanie zbyt wielu elementów w jednej klatce, co powoduje poważne zacinanie się. | mniejsze obciążenie podczas renderowania klatki. Nie używaj algorytmów n2. Używaj wydajnych komponentów do przewijania lub stronicowania, np. biblioteki stronicowania Jetpack. |
| Zablokowana przez inny komponent | Działa inny komponent, np. odbiornik transmisji, który blokuje wątek główny. | W miarę możliwości przenieś z wątku głównego zadania niezwiązane z interfejsem. Uruchamiaj odbiorniki transmisji w innym wątku. |
| Zawieszanie się GPU | Zawieszenie GPU to problem z systemem lub sprzętem, który powoduje zablokowanie renderowania, a w konsekwencji błąd ANR związany z przekazywaniem danych wejściowych. | Zwykle nie można tego naprawić po stronie aplikacji. Jeśli to możliwe, skontaktuj się z zespołem ds. sprzętu, aby rozwiązać problem. |
Jak debugować
Rozpocznij debugowanie, sprawdzając sygnaturę klastra błędów ANR w Konsoli Google Play lub Firebase Crashlytics. Klaster zwykle zawiera najważniejsze ramki, które prawdopodobnie spowodowały błąd ANR.
Poniższy schemat blokowy pokazuje, jak określić przyczynę błędu ANR związanego z przekroczeniem limitu czasu danych wejściowych.
Dane o wydajności w Google Play mogą wykrywać niektóre z tych typowych przyczyn błędów ANR i pomagać w ich usuwaniu. Jeśli na przykład wskaźniki wykryją, że błąd ANR wystąpił z powodu rywalizacji o blokadę, mogą podsumować problem i zalecane rozwiązanie w sekcji Statystyki dotyczącej błędów ANR.
Brak zaznaczonego okna
Zdarzenia takie jak dotyk są wysyłane bezpośrednio do odpowiedniego okna na podstawie testowania trafień, ale zdarzenia takie jak naciśnięcie klawisza wymagają celu. Ten cel jest nazywany oknem docelowym. Na każdym wyświetlaczu jest tylko jedno aktywne okno, zwykle to, z którym użytkownik w danej chwili wchodzi w interakcję. Jeśli nie można znaleźć okna, na którym jest fokus, dane wejściowe powodują błąd ANR związany z brakiem okna z fokusem. Błąd ANR bez okna z ogniskiem to typ błędu ANR związanego z wysyłaniem danych wejściowych.
Domyślny okres oczekiwania: 5 sekund.
Typowe przyczyny
Błędy ANR w przypadku braku aktywnego okna są zwykle spowodowane jednym z tych problemów:
- Aplikacja wykonuje zbyt dużo pracy i zbyt wolno rysuje pierwszą klatkę.
- Głównego okna nie można zaznaczyć. Jeśli okno jest oznaczone symbolem
FLAG_NOT_FOCUSABLE, użytkownik nie może wysyłać do niego zdarzeń klawiszy ani przycisków.
Kotlin
override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) window.addFlags(WindowManager.LayoutParams.FLAG_FLAG_NOT_FOCUSABLE) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); }
Limit czasu odbiornika
Błąd ANR odbiornika występuje, gdy odbiornik nie przetworzy transmisji w odpowiednim czasie. W przypadku odbiorników synchronicznych lub odbiorników, które nie wywołują goAsync(), przekroczenie limitu czasu oznacza, że onReceive() nie zostało ukończone na czas. W przypadku odbiorców asynchronicznych lub odbiorców, którzy wywołują funkcję goAsync(), przekroczenie limitu czasu oznacza, że funkcja PendingResult.finish() nie została wywołana na czas.
Błędy ANR odbiornika transmisji często występują w tych wątkach:
- Wątek główny, jeśli problemem jest powolne uruchamianie aplikacji.
- Wątek uruchamiający odbiornik transmisji, jeśli problemem jest powolny kod
onReceive(). - Wątki robocze transmisji, jeśli problemem jest powolny kod transmisji
goAsync().
Aby uniknąć błędów ANR w przypadku odbiorników transmisji, postępuj zgodnie z tymi sprawdzonymi metodami:
- Upewnij się, że aplikacja uruchamia się szybko, ponieważ jeśli jest uruchamiana w celu obsługi transmisji, jest wliczana do limitu czasu ANR.
- Jeśli używasz funkcji
goAsync(), upewnij się, że funkcjaPendingResult.finish()jest wywoływana szybko. Obowiązuje w nim ten sam limit czasu ANR co w przypadku synchronicznych odbiorników transmisji. - Jeśli używasz
goAsync(), upewnij się, że wątki instancji roboczej nie są współdzielone z innymi długotrwałymi lub blokującymi operacjami. - Rozważ użycie
registerReceiver()do uruchamiania odbiorników transmisji w wątku innym niż główny, aby uniknąć blokowania kodu interfejsu działającego w wątku głównym.
Okresy zawieszenia
Okresy oczekiwania na odbiór transmisji zależą od tego, czy ustawiona jest flaga intencji pierwszoplanowej, oraz od wersji platformy.
| Typ intencji | Android 13 lub starszy | Android 14 lub nowszy |
|---|---|---|
Intencja priorytetu pierwszego planu ( |
10 sekund |
10–20 sekund, w zależności od tego, czy proces jest obciążony |
Intencja priorytetu w tle ( |
60 sekund |
60–120 sekund, w zależności od tego, czy proces jest obciążony |
Aby sprawdzić, czy flaga FLAG_RECEIVER_FOREGROUND jest ustawiona, poszukaj w temacie ANR ciągu „flg=” i sprawdź, czy występuje w nim znak 0x10000000. Jeśli ten bit jest ustawiony, intencja ma ustawioną wartość FLAG_RECEIVER_FOREGROUND, a więc limit czasu jest krótszy.
Przykładowy temat błędu ANR z krótkim czasem oczekiwania na transmisję (10–20 sekund):
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
Przykładowy temat błędu ANR z długim czasem oczekiwania na transmisję (60–120 sekund):
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
Jak mierzone są czasy transmisji
Pomiar czasu trwania transmisji rozpoczyna się, gdy transmisja jest wysyłana z system_server do aplikacji, a kończy się, gdy aplikacja zakończy przetwarzanie transmisji. Jeśli proces aplikacji nie był jeszcze uruchomiony, musi też wykonać zimny start w okresie limitu czasu ANR. W związku z tym powolne uruchamianie aplikacji może powodować błędy ANR odbiornika transmisji.
Ilustracja poniżej pokazuje, że oś czasu ANR odbiornika transmisji jest zgodna z niektórymi procesami aplikacji.
Pomiar czasu oczekiwania na odpowiedź ANR kończy się, gdy odbiornik zakończy przetwarzanie transmisji. Moment ten zależy od tego, czy jest to odbiornik synchroniczny czy asynchroniczny.
- W przypadku odbiorników synchronicznych pomiar zatrzymuje się, gdy funkcja
onReceive()zwraca wartość. - W przypadku odbiorników asynchronicznych pomiar zatrzymuje się po wywołaniu funkcji
PendingResult.finish().
Typowe przyczyny
Oto kilka typowych przyczyn błędów ANR w przypadku odbiorników transmisji i sugerowane rozwiązania.
| Przyczyna | Dotyczy | Co się stało | Proponowane rozwiązanie problemu |
|---|---|---|---|
| Powolne uruchamianie aplikacji | Wszyscy odbiorcy | Uruchamianie aplikacji trwało zbyt długo. | Optymalizacja powolnego uruchamiania aplikacji. |
Nie zaplanowano: onReceive() | Wszyscy odbiorcy | Wątek odbiornika transmisji był zajęty innymi zadaniami i nie mógł uruchomić metody onReceive(). | Nie wykonuj długotrwałych zadań w wątku odbiorcy (lub przenieś odbiorcę do dedykowanego wątku). |
Wolny nośnik onReceive() | Wszystkie odbiorniki, ale głównie synchroniczne | Metoda onReceive() została uruchomiona, ale
została zablokowana lub działała zbyt wolno, więc nie została ukończona na czas. | Zoptymalizuj powolny kod odbiorcy. |
| Zadania odbiornika asynchronicznego nie są zaplanowane | goAsync()odbiorniki, | Metoda onReceive() próbowała wykonać zadanie w zablokowanej puli wątków instancji roboczych, więc zadanie nigdy się nie rozpoczęło. |
Zoptymalizuj wolne lub blokujące wywołania albo użyj różnych wątków dla procesów roboczych transmisji i innych długotrwałych zadań. |
| Instancje robocze działają wolno lub są zablokowane | goAsync() odbiorniki, |
Podczas przetwarzania komunikatu w puli wątków instancji roboczych wystąpiła blokująca lub powolna operacja. Dlatego PendingResult.finish
nie został/a wezwany/a na czas. | Zoptymalizuj powolny kod odbiornika async. |
Zapomniałem(-am) zadzwonić pod numer PendingResult.finish |
goAsync() odbiorniki, |
W ścieżce kodu brakuje wywołania funkcji finish(). |
Upewnij się, że funkcja finish() jest zawsze wywoływana. |
Jak debugować
Na podstawie sygnatury klastra i raportu ANR możesz zlokalizować wątek, w którym działa odbiorca, a następnie konkretny kod, którego brakuje lub który działa wolno.
Poniższy schemat blokowy pokazuje, jak ustalić przyczynę błędu ANR w przypadku odbiornika transmisji.
Znajdowanie kodu odbiornika
W Konsoli Google Play w sygnaturze błędu ANR wyświetlana jest klasa odbiorcy i intencja transmisji. Zwróć uwagę na te kwestie:
cmp=<receiver class>act=<broadcast_intent>
Oto przykład sygnatury ANR odbiornika transmisji:
com.example.app.MyClass.myMethod
Broadcast of Intent { act=android.accounts.LOGIN_ACCOUNTS_CHANGED
cmp=com.example.app/com.example.app.MyAccountReceiver }
Znajdowanie wątku, w którym działa metoda onReceive()
Jeśli używasz Context.registerReceiver do określania niestandardowego modułu obsługi, jest to wątek, w którym działa ten moduł. W przeciwnym razie jest to główny wątek.
Przykład: zadania odbiorcy asynchronicznego nie są zaplanowane
W tej sekcji znajdziesz przykład debugowania błędu ANR w przypadku odbiornika transmisji.
Załóżmy, że sygnatura błędu ANR wygląda tak:
com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }
Na podstawie podpisu wygląda na to, że intencja transmisji to
android.accounts.LOG_ACCOUNTS_CHANGED, a klasa odbiorcy to
com.example.app.MyReceiver.
Z kodu odbiornika możesz się dowiedzieć, że główną pracę związaną z przetwarzaniem tej transmisji wykonuje pula wątków „BG Thread[0,1,2,3]”. Analizując zrzuty stosu, możesz zauważyć, że wszystkie 4 wątki w tle mają ten sam wzorzec: wykonują wywołanie blokujące getDataSync. Wszystkie wątki w tle były zajęte, więc nie można było na czas przetworzyć transmisji, co spowodowało błąd ANR.
BG Thread #0 (tid=26) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture:563)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture:68)
at com.example.app.getDataSync(<MyClass>:152)
...
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at com.google.android.libraries.concurrent.AndroidExecutorsModule.lambda$withStrictMode$5(AndroidExecutorsModule:451)
at com.google.android.libraries.concurrent.AndroidExecutorsModule$$ExternalSyntheticLambda8.run(AndroidExecutorsModule:1)
at java.lang.Thread.run(Thread.java:1012)
at com.google.android.libraries.concurrent.ManagedPriorityThread.run(ManagedPriorityThread:34)
There are several approaches to fix the issue:
- Find out why
getDataSyncis slow and optimize. - Don't run
getDataSyncon all four BG threads. - More generally, ensure that the BG thread pool isn't saturated with long-running operations.
- Use a dedicated thread pool for
goAsyncworker tasks. - Use an unbounded thread pool instead of the bounded BG thread pool
Example: slow app startup
A slow app startup can cause several types of ANRs, especially broadcast
receiver and execute service ANRs. The cause of an
ANR is likely slow app startup if you see ActivityThread.handleBindApplication
in the main thread stacks.
Execute service timeout
An execute service ANR happens when the app's main thread doesn't start a
service in time. Specifically, a service doesn't finish executing
onCreate() and onStartCommand() or onBind() within the
timeout period.
Default timeout period: 20 seconds for foreground service; 200 seconds for
background service. The ANR timeout period includes the app cold start, if
necessary, and calls to onCreate(), onBind(), or onStartCommand().
To avoid execute service ANRs, follow these general best practices:
- Make sure that app startup is fast, since it's counted in the ANR timeout if the app is started to run the service component.
- Make sure that the service's
onCreate(),onStartCommand(), andonBind()methods are fast. - Avoid running any slow or blocking operations on the main thread from other components; these operations can prevent a service from starting quickly.
Common causes
The following table lists common causes of execute service ANRs and suggested fixes.
| Cause | What | Suggested fix |
|---|---|---|
| Slow app startup | The app takes too long to perform a cold start. | Optimize slow app start. |
Slow onCreate(), onStartCommand(), or
onBind() |
The service component's onCreate(),
onStartCommand(), or onBind() method takes too long to
execute on the main thread. |
Optimize slow code. Move slow operations off the critical path where possible. |
Not scheduled (main thread blocked before onStart()) |
The app's main thread is blocked by another component before the service can be started. | Move other component's work off the main thread. Optimize other component's blocking code. |
How to debug
From the cluster signature and ANR report in Google Play Console or Firebase Crashlytics, you can often determine the cause of the ANR based on what the main thread is doing.
The following flow chart describes how to debug an execute service ANR.
If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:
Find the service component class in the ANR signature. In Google Play Console, the service component class is shown in the ANR signature. In the following example ANR details, it's
com.example.app/MyService.com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly Executing service com.example.app/com.example.app.MyServiceDetermine whether the slow or block operation is part of app startup, the service component, or elsewhere by checking for the following important function call(s) in the main threads.
Function call(s) in main thread stacks What it means android.app.ActivityThread.handleBindApplicationApp was starting up, so the ANR was caused by slow app start. <ServiceClass>.onCreate()
[...]
android.app.ActivityThread.handleCreateService
Service was being created, so the ANR was likely caused by slow onCreate()code.<ServiceClass>.onBind()
[...]
android.app.ActivityThread.handleBindService
Service was being bound, so the ANR was likely caused by slow onBind()code.<ServiceClass>.onStartCommand()
[...]
android.app.ActivityThread.handleServiceArgs
Service was being started, so the ANR was likely caused by slow onStartCommand()code.For example, if the
onStartCommand()method in theMyServiceclass is slow, the main threads will look like this:at com.example.app.MyService.onStartCommand(FooService.java:25) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4820) at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(unavailable:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2289) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:205) at android.os.Looper.loop(Looper.java:294) at android.app.ActivityThread.main(ActivityThread.java:8176) at java.lang.reflect.Method.invoke(Native method:0)Jeśli nie widzisz żadnych ważnych wywołań funkcji, istnieją jeszcze 2 możliwości:
- Usługa działa lub jest wyłączana, co oznacza, że zrzuty stosu są wykonywane zbyt późno. W takim przypadku możesz zignorować błąd ANR jako fałszywie pozytywny.
- Działa inny komponent aplikacji, np. odbiornik transmisji. W tym przypadku wątek główny jest prawdopodobnie zablokowany w tym komponencie, co uniemożliwia uruchomienie usługi.
Jeśli widzisz wywołanie kluczowej funkcji i możesz określić, gdzie występuje błąd ANR, sprawdź pozostałe stosy wątku głównego, aby znaleźć powolną operację i ją zoptymalizować lub przenieść poza ścieżkę krytyczną.
- Zadbaj o szybkie uruchamianie aplikacji, ponieważ jest ono wliczane do limitu czasu ANR, jeśli aplikacja jest uruchamiana w celu uruchomienia dostawcy treści.
- Upewnij się, że zapytania dostawcy treści są szybkie.
- Nie wykonuj wielu jednoczesnych wywołań blokujących, które mogą zablokować wszystkie wątki aplikacji.
- Ciche zawieszenia interfejsu: interfejs aplikacji przestaje reagować na dane wejściowe użytkownika.
- Awaria: aplikacja może ulec awarii z błędem
illegalStateException. - Błędy ANR: system może spowodować przekroczenie limitu czasu błędu ANR.
- Planowanie przejść:
ViewRootImplplanuje przejście – układ i rysowanie – przez wysłanie bariery synchronizacji do kolejki komunikatów wątku interfejsuMessageQueue. Ta bariera wstrzymuje normalne przetwarzanie wiadomości, aby układ interfejsu mógł mieć wyższy priorytet. - Wyścig: gdy wiele wątków próbuje jednocześnie unieważnić widok, rywalizują o zaplanowanie tego przejścia. Oba wątki mogą z powodzeniem wstawić barierę synchronizacji, ale platforma przechowuje token tylko dla jednego z nich.
- Zawieszenie interfejsu: podczas wykonywania przejścia usuwana jest tylko jedna zapisana bariera. Drugie, „wyciekające” bariery pozostają w kolejce bezterminowo, trwale blokując wątek UI przed przetwarzaniem synchronicznych wiadomości.
- Wchodź w interakcje tylko z obiektami
View, w tym odczytuj właściwości takie jakwidthlubheightalbo ustawiaj właściwości takie jakTextView's text, w wątku, w którym utworzono hierarchię widoków. Jest to prawie zawsze wątek główny lub wątek interfejsu. - Jeśli nie masz pewności, że podczas uzyskiwania dostępu do widoków działasz w wątku interfejsu, załóż, że jest to niebezpieczne. Jeśli np. używasz
Coroutinesdo pracy w tle, musisz przełączyć się na główny dyspozytor przed manipulowaniem obiektamiView. ContextCompat#getMainExecutor(android.content.Context)View.post(Runnable)Activity.runOnUiThread(Runnable)- Zainstaluj wersję aplikacji z możliwością debugowania na urządzeniu z Androidem 17 lub nowszym.
- Otwórz na urządzeniu aplikację Ustawienia i wybierz System > Zaawansowane > Opcje programisty > Zmiany w zakresie zgodności aplikacji.
- Wybierz aplikację z listy.
- Na liście zmian znajdź przełącznik
ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APISi włącz go. - Telemetria i śledzenie: ten odbiornik umożliwia aplikacji otrzymywanie wywołań zwrotnych, gdy interfejs View API jest wywoływany z nieprawidłowego wątku, co ułatwia rejestrowanie danych telemetrycznych i wykrywanie błędów.
- Wykonanie wbudowane: detektor jest wywoływany wbudowanie, co umożliwia przechwytywanie i sprawdzanie dokładnego zrzutu stosu w momencie wystąpienia naruszenia.
- Problem w całym systemie Proces nie został zaplanowany z powodu dużego obciążenia systemu lub problemu z serwerem systemowym.
- Late stack dump Wątek został odzyskany w krótkim okresie między wywołaniem błędu ANR a zrzuceniem stosów. Opóźnienie na Pixelach z Androidem 13 wynosi około 100 ms, ale może przekraczać 1 s. Opóźnienie na urządzeniach Pixel z Androidem 14 wynosi zwykle poniżej 10 ms.
- Błędne przypisanie wątku. Wątek użyty do utworzenia podpisu ANR nie był wątkiem, który spowodował ANR.
- Wyświetl naruszenie wątkowania. Jeśli aplikacja modyfikuje widok w wątku w tle, może to spowodować sytuację wyścigu w wewnętrznych elementach widoku, który uniemożliwi uruchamianie zadań w wątku UI.
- Przeciążenie systemu: oceniaj ogólne obciążenie zasobów urządzenia, np. niedobór procesora, pamięci lub wejścia/wyjścia w całym systemie, jako główną przyczynę braku reakcji.
- Błędne przypisanie wątku: sprawdzanie wątków roboczych i wątków powiązań pod kątem zakleszczeń, rywalizacji o blokady wpływających na wątek główny lub zawieszonych wątków w tle przetwarzających komponenty asynchroniczne (np.
goAsync()). - View Threading Violations (Wyświetl naruszenia wątków): skanuje wątki w tle pod kątem niezgodnych z prawem modyfikacji hierarchii widoków interfejsu, które mogą spowodować osierocenie bariery synchronizacji w kolejce komunikatów i trwałe zablokowanie wszystkich komunikatów synchronicznych.
- Pobieranie stosu trwa zbyt długo i przekracza limit czasu.
- Proces został przerwany lub zakończony, zanim wykonano zrzuty stosu.
Więcej informacji o usługach znajdziesz na tych stronach:
Dostawca treści nie odpowiada
Błąd ANR dostawcy treści występuje, gdy zdalny dostawca treści potrzebuje więcej czasu niż okres oczekiwania na odpowiedź na zapytanie i zostaje zamknięty.
Domyślny okres oczekiwania: określony przez dostawcę treści za pomocą tagu ContentProviderClient.setDetectNotResponding. Okres oczekiwania na ANR obejmuje łączny czas wykonywania zapytania do zdalnego dostawcy treści, w tym uruchomienie zdalnej aplikacji, jeśli nie była jeszcze uruchomiona.
Aby uniknąć błędów ANR u dostawców treści, postępuj zgodnie z tymi sprawdzonymi metodami:
Typowe przyczyny
W tabeli poniżej znajdziesz typowe przyczyny błędów ANR u dostawców treści i sugerowane rozwiązania.
| Przyczyna | Co się dzieje | Sygnał | Proponowane rozwiązanie problemu |
|---|---|---|---|
| Powolne zapytanie dostawcy treści | Dostawca treści zbyt długo wykonuje działanie lub jest zablokowany. | Ramka android.content.ContentProvider$Transport.query znajduje się w wątku spoiwa. |
Optymalizacja zapytania dostawcy treści. Sprawdź, co blokuje wątek w binderze. |
| Powolne uruchamianie aplikacji | Aplikacja dostawcy treści zbyt długo się uruchamia. | Klatka ActivityThread.handleBindApplication znajduje się w głównym wątku. |
Zoptymalizuj uruchamianie aplikacji. |
| Wyczerpanie wątków łącznika – wszystkie wątki łącznika są zajęte | Wszystkie wątki bindera są zajęte obsługą innych żądań synchronicznych, więc wywołanie bindera dostawcy treści nie może zostać wykonane. | Aplikacja nie uruchamia się, wszystkie wątki bindera są zajęte, a dostawca treści nie działa. | Zmniejsz obciążenie nici segregatora. Oznacza to, że należy wykonywać mniej synchronicznych połączeń wychodzących lub mniej pracować podczas obsługi połączeń przychodzących. |
Jak debugować
Aby debugować błąd ANR mechanizmu dostarczania treści za pomocą podpisu klastra i raportu ANR w Konsoli Google Play lub Firebase Crashlytics, sprawdź, co robią wątek główny i wątki binder.
Poniższy schemat blokowy opisuje, jak debugować błąd ANR dostawcy treści:
Poniższy fragment kodu pokazuje, jak wygląda wątek bindera, gdy jest zablokowany z powodu powolnego zapytania do dostawcy treści. W tym przypadku zapytanie dostawcy treści oczekuje na blokadę podczas otwierania bazy danych.
binder:11300_2 (tid=13) Blocked
Waiting for osm (0x01ab5df9) held by at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers:182)
at com.example.app.MyClass.blockingGetOpenDatabase(FooClass:171)
[...]
at com.example.app.MyContentProvider.query(MyContentProvider.java:915)
at android.content.ContentProvider$Transport.query(ContentProvider.java:292)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:107)
at android.os.Binder.execTransactInternal(Binder.java:1339)
at android.os.Binder.execTransact(Binder.java:1275)
Poniższy fragment kodu pokazuje, jak wygląda wątek główny, gdy jest zablokowany z powodu powolnego uruchamiania aplikacji. W tym przypadku aplikacja uruchamia się powoli z powodu rywalizacji o blokadę podczas inicjowania Daggera.
main (tid=1) Blocked
[...]
at dagger.internal.DoubleCheck.get(DoubleCheck:51)
- locked 0x0e33cd2c (a qsn)at dagger.internal.SetFactory.get(SetFactory:126)
at com.myapp.Bar_Factory.get(Bar_Factory:38)
[...]
at com.example.app.MyApplication.onCreate(DocsApplication:203)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6991)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2235)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8170)
at java.lang.reflect.Method.invoke(Native method:0)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Wolna odpowiedź na zadanie
Błąd ANR związany z powolną odpowiedzią zadania występuje, gdy aplikacja zbyt długo odpowiada na wywołanie JobService.onStartJob() lub JobService.onStopJob() albo zbyt długo wyświetla powiadomienie za pomocą JobService.setNotification(). Sugeruje to, że główny wątek aplikacji jest zablokowany podczas wykonywania innych czynności.
Jeśli problem dotyczy JobService.onStartJob() lub JobService.onStopJob(), sprawdź, co się dzieje w głównym wątku. Jeśli problem dotyczy JobService.setNotification(), zadzwoń jak najszybciej.
Nie wykonuj wielu działań przed wyświetleniem powiadomienia.
Wyświetlanie naruszenia wątkowania
Jeśli aplikacja modyfikuje widok w wątku w tle, może to spowodować wyścig w stanie wewnętrznym hierarchii widoków. To naruszenie może uniemożliwić wątkowi głównemu (interfejsu) wykonywanie synchronicznych wiadomości, co prowadzi do poważnych problemów ze stabilnością:
To naruszenie wątkowości jest jedną z głównych przyczyn zrzutów stosu błędów ANR, w których wątek główny wydaje się nieaktywny (np. zarejestrowany w funkcji „nativePollOnce” lub „main thread idle”).
Przeciek bariery synchronizacji
Widoki mogą zostać unieważnione pośrednio przez zmianę ich stanu (np. wywołanie TextView.setText, View.setVisibility) lub bezpośrednio przez wywołanie View.invalidate lub View.requestLayout. Gdy widok zostanie unieważniony,ViewRootImpl zaplanuje przejście w celu wykonania pomiaru, układu i rysowania, aby zaktualizować stan interfejsu.
W związku z tym interfejs użytkownika przestaje odpowiadać. Ponieważ MessageQueue nie może przetwarzać żadnych synchronicznych wiadomości poza wyciekłymi barierami, główny wątek przechodzi w stan bezczynności. Ten problem zwykle objawia się błędem ANR z symbolem nativePollOnce w zrzucie stosu.
Aby uniknąć naruszeń związanych z wątkami wyświetleń, postępuj zgodnie z tymi sprawdzonymi metodami:
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { val data = myRepository.getData() withContext(Dispatchers.Main) { // Switch context to Main myTextView.text = data.title } }
Aby ułatwić Ci stosowanie sprawdzonych metod, Android oferuje kilka sposobów uzyskiwania dostępu do wątku interfejsu z innych wątków:
Popularne moduły wczytywania obrazów, frameworki rozszerzeń reaktywnych, biblioteki magistrali zdarzeń i inne rozwiązania związane z wątkami zwykle oferują sposoby obserwowania określonych zdarzeń w wątku głównym. Jest to przydatne w przypadku obserwatorów i detektorów zdarzeń, które muszą pobierać i ustawiać stan widoków.
Jak debugować
Android 17 wprowadza nowe narzędzia, które pomagają identyfikować, śledzić i rozwiązywać naruszenia wątków widoku podczas programowania.
1. Narzędzia systemu sprawdzania zgodności
Narzędzia systemu sprawdzania zgodności umożliwiają programistom aplikacji włączanie i wyłączanie poszczególnych zmian w zachowaniu za pomocą opcji programisty lub ADB. Aby włączyć lub wyłączyć zmiany w zachowaniu związanym z naruszeniem wątkowania widoku, wykonaj te czynności:
Możesz też włączyć lub wyłączyć flagę za pomocą ADB:
$ adb shell am compat enable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
$ adb shell am compat disable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
Włączenie tej flagi powoduje, że aplikacja zgłasza wyjątek i ulega awarii za każdym razem, gdy interfejs View API jest wywoływany z niewłaściwego wątku. Pomaga to wykrywać i naprawiać problemy związane z naruszeniem zasad dotyczących wątków w przypadku widoków.
2. CalledFromWrongThreadListener API
Możesz też wdrożyć interfejs global
View#registerCalledFromWrongThreadListener API, aby programowo wykrywać nieprawidłowy dostęp do wątków.
android {
//...
compileSdk = 37
}
val listener = object : View.CalledFromWrongThreadListener { override fun onCalledFromWrongThread() { // Handle the issue, e.g. crash if this is a dev build, or log an event // e.g. Log.d(TAG, "CalledFromWrongThread: ${Exception().stackTraceToString()}") // Unregister the listener to avoid redundant notifications for the same issue View.unregisterCalledFromWrongThreadListener(this) } } View.registerCalledFromWrongThreadListener(listener)
nativePollOnce
Jeśli w śladach ANR zobaczysz ramkę „nativePollOnce” lub „message queue idle”, często oznacza to, że podejrzewany wątek, który nie odpowiadał, był w rzeczywistości bezczynny i czekał na komunikaty z pętli. W Konsoli Google Play szczegóły błędów ANR wyglądają tak:
Native method - android.os.MessageQueue.nativePollOnce
Executing service com.example.app/com.example.app.MyService
Jeśli np. główny wątek jest bezczynny, stosy wyglądają tak:
"main" tid=1 NativeMain threadIdle
#00 pc 0x00000000000d8b38 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
#01 pc 0x0000000000019d88 /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
#02 pc 0x0000000000019c68 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
#03 pc 0x000000000011409c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (Native method)
at android.os.MessageQueue.next (MessageQueue.java:339) at android.os.Looper.loop (Looper.java:208)
at android.app.ActivityThread.main (ActivityThread.java:8192)
at java.lang.reflect.Method.invoke (Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:626)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1015)
Podejrzany wątek może być nieaktywny z kilku powodów:
Przyczyny błędów ANR „nativePollOnce” różnią się w zależności od typu błędu ANR, dlatego zdiagnozowanie konkretnej kategorii błędu ANR może pomóc w określeniu działań, które należy podjąć, aby rozwiązać problem w aplikacji.
| Kategoria | Przyczyna | Proponowane rozwiązanie problemu |
|---|---|---|
| Czas oczekiwania na wysłanie danych wejściowych | Problem dotyczący całego systemu późny zrzut stosu |
Nie musisz nic robić. |
| Brak zaznaczonego okna | Problem w całym systemie Późny zrzędu stosu Wyświetl naruszenie wątkowania |
Sprawdź bazę kodu pod kątem naruszeń wątków widoku. |
| Limit czasu odbiornika | Późny zrzut stosu Problem w całym systemie Błędne przypisanie wątku Wyświetl naruszenie wątkowości |
Sprawdź odpowiednie wątki w zrzucie stosu. Sprawdź bazę kodu pod kątem naruszeń wątków widoku. |
| Czas oczekiwania na wykonanie usługi | Późny zrzut stosu Problem w całym systemie Wyświetl naruszenie wątków |
Sprawdź bazę kodu pod kątem naruszeń wątków widoku. |
| Dostawca treści nie odpowiada | Późny zrzędu wątków Problem w całym systemie Błędne przypisanie wątku |
Sprawdź odpowiednie wątki w zrzucie stosu. |
Oto zalecane czynności, które należy wykonać, aby przeanalizować błędy ANR w przypadku funkcji nativePollOnce.
Brak ramek stosu
Niektóre raporty ANR nie zawierają stosów z błędem ANR, co oznacza, że podczas generowania raportu ANR nie udało się zrzucić stosu. Brakujące ramki stosu mogą mieć kilka przyczyn:
[...]
--- CriticalEventLog ---
capacity: 20
timestamp_ms: 1666030897753
window_ms: 300000
libdebuggerd_client: failed to read status response from tombstoned: timeout reached?
----- Waiting Channels: pid 7068 at 2022-10-18 02:21:37.<US_SOCIAL_SECURITY_NUMBER>+0800 -----
[...]
W przypadku błędów ANR bez ramek stosu nie można podjąć żadnego działania na podstawie podpisu klastra ani raportu ANR. Aby rozwiązać problem, poszukaj innych klastrów aplikacji, ponieważ jeśli problem jest wystarczająco duży, zwykle ma własny klaster, w którym znajdują się ramki stosu. Możesz też sprawdzić ślady Perfetto.
Znane problemy
Utrzymywanie w procesie aplikacji timera, który ma na celu zakończenie obsługi transmisji przed wywołaniem błędu ANR, może nie działać prawidłowo ze względu na asynchroniczny sposób monitorowania błędów ANR przez system.