Diagnozowanie i naprawianie błędów ANR

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_server pod 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.

Rysunek 1. Jak debugować błąd ANR związany z wysyłaniem 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.

Rysunek 2. Wykrywanie błędów ANR w Android Vitals.

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 funkcja PendingResult.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

(FLAG_RECEIVER_FOREGROUND zestaw)

10 sekund

10–20 sekund, w zależności od tego, czy proces jest obciążony

Intencja priorytetu w tle

(FLAG_RECEIVER_FOREGROUND nie ustawiono)

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.

Rysunek 3. Oś czasu błędu ANR odbiornika.

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().
Rysunek 4. Punkty końcowe pomiaru limitu czasu ANR dla odbiorników synchronicznych i asynchronicznych.

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.

Rysunek 5. Jak debugować błąd ANR 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 getDataSync is slow and optimize.
  • Don't run getDataSync on 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 goAsync worker 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(), and onBind() 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.

Figure 6. 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:

  1. 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.MyService
    
  2. Determine 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.handleBindApplication App 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 the MyService class 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.
  3. 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ą.

  4. 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:

    • 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.

    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:

    Rysunek 7. 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

    Wypróbuj tworzenie
    Jetpack Compose to zalecany zestaw narzędzi interfejsu na Androida.

    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ą:

    • 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.

    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.

    1. Planowanie przejść: ViewRootImpl planuje przejście – układ i rysowanie – przez wysłanie bariery synchronizacji do kolejki komunikatów wątku interfejsu MessageQueue. Ta bariera wstrzymuje normalne przetwarzanie wiadomości, aby układ interfejsu mógł mieć wyższy priorytet.
    2. 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.
    3. 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.

    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:

    • Wchodź w interakcje tylko z obiektami View, w tym odczytuj właściwości takie jak width lub height albo ustawiaj właściwości takie jak TextView'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 obiektami View.

    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:

    1. Zainstaluj wersję aplikacji z możliwością debugowania na urządzeniu z Androidem 17 lub nowszym.
    2. Otwórz na urządzeniu aplikację Ustawienia i wybierz System > Zaawansowane > Opcje programisty > Zmiany w zakresie zgodności aplikacji.
    3. Wybierz aplikację z listy.
    4. Na liście zmian znajdź przełącznik ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS i włącz go.

    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.

    • 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.
    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:

    • 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.

    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.

    1. 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.
    2. 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()).
    3. 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.

    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:

    • Pobieranie stosu trwa zbyt długo i przekracza limit czasu.
    • Proces został przerwany lub zakończony, zanim wykonano zrzuty stosu.
    [...]
    
    --- 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.