Wenn der UI-Thread einer Android-App zu lange blockiert wird, sendet das System einen Fehler vom Typ „App antwortet nicht“ (ANR). Auf dieser Seite werden die verschiedenen Arten von ANR-Fehlern, die Diagnose und Vorschläge zur Behebung beschrieben. Alle aufgeführten Standardzeitbereiche für Zeitüberschreitungen gelten für AOSP- und Pixel-Geräte. Diese Zeiten können je nach Erstausrüster variieren.
Bei der Ermittlung der Ursache von ANR-Fehlern ist es hilfreich, zwischen System- und App-Problemen zu unterscheiden.
Wenn sich das System in einem schlechten Zustand befindet, können die folgenden Probleme ANRs verursachen:
- Vorübergehende Probleme auf dem Systemserver führen in der Regel dazu, dass schnelle Binder-Aufrufe langsam sind.
- Probleme mit dem Systemserver und eine hohe Geräteauslastung führen dazu, dass App-Threads nicht geplant werden.
Wenn verfügbar, ist es eine gute Möglichkeit, zwischen System- und App-Problemen zu unterscheiden, Perfetto-Traces zu verwenden:
- Sehen Sie sich den Threadstatus in Perfetto an, um festzustellen, ob der Hauptthread der App geplant ist. Er sollte „running“ oder „runnable“ sein.
- Sehen Sie sich die
system_server-Threads an, um Probleme wie Sperrkonflikte zu finden. - Bei langsamen Binder-Aufrufen sehen Sie sich den Antwort-Thread an, falls vorhanden, um den Grund für die Verzögerung zu ermitteln.
Zeitlimit für die Eingabeübermittlung
ANRs bei der Eingabeübermittlung treten auf, wenn der Hauptthread der App nicht rechtzeitig auf ein Eingabeereignis wie Wischen oder Drücken einer Taste reagiert. Da die App im Vordergrund ist, wenn Zeitüberschreitungen bei der Eingabeübermittlung auftreten, sind sie fast immer für den Nutzer sichtbar und es ist sehr wichtig, sie zu beheben.
Standardzeitlimit: 5 Sekunden.
ANR-Fehler bei der Eingabeübermittlung werden in der Regel durch Probleme im Hauptthread verursacht. Wenn der Hauptthread blockiert wurde, weil er auf das Abrufen einer Sperre gewartet hat, kann auch der Thread, der die Sperre hält, beteiligt sein.
So vermeiden Sie ANRs beim Dispatch von Eingaben:
- Führen Sie keine blockierenden oder lang andauernden Vorgänge im Hauptthread aus. Verwenden Sie
StrictMode, um versehentliche Aktivitäten im Hauptthread abzufangen. - Sperrenkonflikte zwischen dem Hauptthread und anderen Threads minimieren.
- Minimieren Sie die Arbeit, die nicht mit der Benutzeroberfläche zusammenhängt, im Hauptthread, z. B. beim Verarbeiten von Broadcasts oder beim Ausführen von Diensten.
Häufige Ursachen
Hier sind einige häufige Ursachen und Lösungsvorschläge für ANRs beim Input Dispatch.
| Ursache | Was passiert? | Vorgeschlagene Korrekturen |
|---|---|---|
| Langsamer Binder-Aufruf | Der Hauptthread führt einen langen synchronen Binder-Aufruf aus. | Lagern Sie den Aufruf aus dem Haupt-Thread aus oder versuchen Sie, den Aufruf zu optimieren, wenn Sie die API besitzen. |
| Viele aufeinanderfolgende Binder-Aufrufe | Der Hauptthread führt viele aufeinanderfolgende synchrone Binder-Aufrufe aus. | Führen Sie keine Binder-Aufrufe in einer engen Schleife aus. |
| Blockieren von E/A | Der Hauptthread führt einen blockierenden E/A-Aufruf aus, z. B. für den Datenbank- oder Netzwerkzugriff. | Verschieben Sie alle blockierenden E/A-Vorgänge aus dem Hauptthread. |
| Sperrenkonflikt | Der Hauptthread ist blockiert und wartet auf den Erhalt einer Sperre. | Reduzieren Sie die Sperrenkonflikte zwischen dem Hauptthread und anderen Threads. Langsamen Code im anderen Thread optimieren |
| Teurer Frame | Zu viel Rendering in einem einzelnen Frame, was zu starken Rucklern führt. | Weniger Aufwand beim Rendern des Frames Verwenden Sie keine n2-Algorithmen. Verwenden Sie effiziente Komponenten für Vorgänge wie Scrollen oder Paging, z. B. die Paging-Bibliothek von Jetpack. |
| Durch andere Komponente blockiert | Eine andere Komponente, z. B. ein Übertragungsempfänger, wird ausgeführt und blockiert den Hauptthread. | Lagern Sie Nicht-UI-Arbeiten so weit wie möglich aus dem Hauptthread aus. Broadcast-Empfänger in einem anderen Thread ausführen |
| GPU hängt | Ein GPU-Hänger ist ein System- oder Hardwareproblem, das dazu führt, dass das Rendern blockiert wird und daher ein ANR-Fehler beim Dispatching von Eingaben auftritt. | Leider gibt es in der Regel keine Korrekturen auf der App-Seite. Wenden Sie sich nach Möglichkeit an das Hardwareteam, um das Problem zu beheben. |
Fehlerbehebung
Beginnen Sie mit dem Debugging, indem Sie sich die ANR-Clustersignatur in der Google Play Console oder in Firebase Crashlytics ansehen. Der Cluster enthält in der Regel die wichtigsten Frames, die vermutlich den ANR-Fehler verursacht haben.
Das folgende Flussdiagramm zeigt, wie Sie die Ursache für einen ANR-Dispatch aufgrund eines Eingabe-Timeouts ermitteln.
Mit Play Vitals können einige dieser häufigen ANR-Ursachen erkannt und behoben werden. Wenn beispielsweise anhand von Vitalparametern erkannt wird, dass ein ANR aufgrund von Konflikten bei der Sperrverwaltung aufgetreten ist, kann das Problem und die empfohlene Lösung im Abschnitt Erkenntnisse für ANR zusammengefasst werden.
Kein fokussiertes Fenster
Während Ereignisse wie Berührungen basierend auf Hit-Tests direkt an das entsprechende Fenster gesendet werden, benötigen Ereignisse wie Tasten ein Ziel. Dieses Ziel wird als fokussiertes Fenster bezeichnet. Pro Display gibt es nur ein fokussiertes Fenster. Das ist in der Regel das Fenster, mit dem der Nutzer gerade interagiert. Wenn kein fokussiertes Fenster gefunden werden kann, löst die Eingabe einen ANR ohne fokussiertes Fenster aus. Ein ANR ohne fokussiertes Fenster ist ein ANR vom Typ „Eingabe-Dispatch“.
Standardzeitlimit: 5 Sekunden.
Häufige Ursachen
ANRs ohne fokussiertes Fenster werden in der Regel durch eines der folgenden Probleme verursacht:
- Die App führt zu viele Aufgaben aus und ist zu langsam, um den ersten Frame zu rendern.
- Das Hauptfenster ist nicht fokussierbar. Wenn ein Fenster mit
FLAG_NOT_FOCUSABLEgekennzeichnet ist, kann der Nutzer keine Tasten- oder Schaltflächenereignisse an das Fenster senden.
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); }
Zeitüberschreitung des Übertragungsempfängers
Ein ANR-Fehler bei einem Übertragungsempfänger tritt auf, wenn ein Übertragungsempfänger einen Broadcast nicht rechtzeitig verarbeitet. Bei synchronen Empfängern oder Empfängern, die goAsync() nicht aufrufen, bedeutet ein Zeitüberschreitungsfehler, dass onReceive() nicht rechtzeitig abgeschlossen wurde. Bei asynchronen Empfängern oder Empfängern, die goAsync() aufrufen, bedeutet ein Zeitüberschreitungsfehler, dass PendingResult.finish() nicht rechtzeitig aufgerufen wurde.
ANRs für Übertragungsempfänger treten häufig in diesen Threads auf:
- Hauptthread, wenn das Problem ein langsamer App-Start ist.
- Übertragungsempfänger, der im Thread ausgeführt wird, wenn das Problem langsamer
onReceive()-Code ist. - Broadcast-Worker-Threads, wenn das Problem langsamer
goAsync()-Broadcast-Code ist.
So vermeiden Sie ANRs bei Übertragungsempfängern:
- Achten Sie darauf, dass die App schnell startet, da dies in das ANR-Zeitlimit einbezogen wird, wenn die App gestartet wird, um den Broadcast zu verarbeiten.
- Wenn
goAsync()verwendet wird, mussPendingResult.finish()schnell aufgerufen werden. Hier gilt dasselbe ANR-Zeitlimit wie für synchrone Broadcast-Empfänger. - Wenn
goAsync()verwendet wird, achten Sie darauf, dass der oder die Arbeitsthreads nicht für andere lang andauernde oder blockierende Vorgänge freigegeben sind. - Verwenden Sie
registerReceiver(), um Broadcast-Receiver in einem anderen als dem Hauptthread auszuführen, damit UI-Code, der im Hauptthread ausgeführt wird, nicht blockiert wird.
Zeitüberschreitungszeiträume
Die Zeitüberschreitungszeiträume für Broadcast-Empfang hängen davon ab, ob das Flag für die Absicht im Vordergrund festgelegt ist, und von der Plattformversion.
| Intent-Typ | Android 13 und niedriger | Android 14 und höher |
|---|---|---|
Intent mit Vordergrundpriorität ( |
10 Sekunden |
10–20 Sekunden, je nachdem, ob der Prozess CPU-lastig ist |
Intent mit Hintergrundpriorität ( |
60 Sekunden |
60–120 Sekunden, je nachdem, ob der Prozess CPU-hungrig ist |
Ob das Flag FLAG_RECEIVER_FOREGROUND gesetzt ist, erkennen Sie daran, dass im ANR-Betreff „flg=“ steht und 0x10000000 vorhanden ist. Wenn dieses Bit gesetzt ist, ist für den Intent FLAG_RECEIVER_FOREGROUND festgelegt und das Zeitlimit ist kürzer.
Beispiel für eine ANR-Betreffzeile mit kurzem Broadcast-Zeitlimit (10–20 Sekunden):
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
Beispiel für ANR-Betreff mit langem Broadcast-Zeitlimit (60–120 Sekunden):
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
So werden Sendezeiten gemessen
Die Messung der Broadcast-Dauer beginnt, wenn der Broadcast von system_server an die App gesendet wird, und endet, wenn die App die Verarbeitung des Broadcasts abgeschlossen hat. Wenn der App-Prozess noch nicht ausgeführt wurde, muss er auch innerhalb des ANR-Zeitlimits einen Kaltstart durchführen. Ein langsamer App-Start kann daher zu ANR-Fehlern bei Übertragungsempfängern führen.
Die folgende Abbildung veranschaulicht, dass die ANR-Zeitachse für Übertragungsempfänger mit bestimmten App-Prozessen übereinstimmt.
Die ANR-Zeitüberschreitungsmessung endet, wenn der Empfänger die Broadcast-Nachricht verarbeitet hat. Wann genau das passiert, hängt davon ab, ob es sich um einen synchronen oder asynchronen Empfänger handelt.
- Bei synchronen Empfängern wird die Messung beendet, wenn
onReceive()zurückgegeben wird. - Bei asynchronen Empfängern wird die Analyse beendet, wenn
PendingResult.finish()aufgerufen wird.
Häufige Ursachen
Hier sind einige häufige Ursachen und Lösungsvorschläge für ANR-Fehler bei Übertragungsempfängern.
| Ursache | Applies to | Hintergrund | Korrekturvorschlag |
|---|---|---|---|
| Langsamer App-Start | Alle Empfänger | Der Kaltstart der App hat zu lange gedauert. | Langsamen App-Start optimieren |
onReceive() nicht geplant | Alle Empfänger | Der Übertragungsempfänger-Thread war mit anderen Aufgaben beschäftigt und konnte die Methode onReceive() nicht starten. | Führen Sie keine zeitaufwendigen Aufgaben im Empfänger-Thread aus (oder verschieben Sie den Empfänger in einen dedizierten Thread). |
Langsames Speichergerät (onReceive()) | Alle Empfänger, aber hauptsächlich synchrone | Die onReceive()-Methode wurde gestartet, aber blockiert oder verlangsamt, sodass sie nicht rechtzeitig abgeschlossen wurde. | Langsam laufenden Empfängercode optimieren |
| Asynchrone Empfängeraufgaben nicht geplant | goAsync()
Empfänger | Mit der Methode onReceive() wurde versucht, Arbeit in einem blockierten Arbeitsthread-Pool auszuführen. Die Arbeit wurde daher nie gestartet. |
Optimiere langsame oder blockierende Aufrufe oder verwende verschiedene Threads für Broadcast-Worker und andere zeitaufwendige Aufgaben. |
| Worker langsam oder blockiert | goAsync() Empfänger |
Bei der Verarbeitung des Broadcasts gab es einen blockierenden oder langsamen Vorgang in einem der Arbeitsthreads. PendingResult.finish wurde also nicht rechtzeitig aufgerufen. | Langsame async-Empfängercodes optimieren. |
Ich habe vergessen, PendingResult.finish anzurufen |
goAsync() Empfänger |
Im Codepfad fehlt ein Aufruf von finish(). |
Sorgen Sie dafür, dass finish() immer aufgerufen wird. |
Fehlerbehebung
Anhand der Clustersignatur und des ANR-Berichts können Sie den Thread ermitteln, in dem der Receiver ausgeführt wird, und dann den spezifischen Code, der fehlt oder langsam ausgeführt wird.
Das folgende Flussdiagramm zeigt, wie du die Ursache eines ANR für Broadcast-Receiver ermittelst.
Empfängercode finden
In der Google Play Console werden die Empfängerklasse und die Broadcast-Intention in der ANR-Signatur angezeigt. Achten Sie auf Folgendes:
cmp=<receiver class>act=<broadcast_intent>
Hier ist ein Beispiel für eine ANR-Signatur für einen Übertragungsempfänger:
com.example.app.MyClass.myMethod
Broadcast of Intent { act=android.accounts.LOGIN_ACCOUNTS_CHANGED
cmp=com.example.app/com.example.app.MyAccountReceiver }
Thread suchen, in dem die Methode „onReceive()“ ausgeführt wird
Wenn Sie Context.registerReceiver verwenden, um einen benutzerdefinierten Handler anzugeben, ist es der Thread, in dem dieser Handler ausgeführt wird. Andernfalls ist es der Hauptthread.
Beispiel: Asynchrone Empfängeraufgaben werden nicht geplant
In diesem Abschnitt wird anhand eines Beispiels beschrieben, wie Sie einen ANR-Fehler bei einem Übertragungsempfänger debuggen.
Angenommen, die ANR-Signatur sieht so aus:
com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }
Anhand der Signatur sieht es so aus, als ob die Broadcast-Intent android.accounts.LOG_ACCOUNTS_CHANGED und die Empfängerklasse com.example.app.MyReceiver ist.
Aus dem Empfängercode geht hervor, dass der Thread-Pool „BG Thread[0,1,2,3]“ die Hauptarbeit für die Verarbeitung dieses Broadcasts übernimmt. Anhand der Stackdumps lässt sich erkennen, dass alle vier Hintergrundthreads (BG) dasselbe Muster aufweisen: Sie führen einen blockierenden Aufruf aus, getDataSync. Da alle BG-Threads ausgelastet waren, konnte die Broadcast-Nachricht nicht rechtzeitig verarbeitet werden, was zu einem ANR führte.
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)Wenn Sie keine der wichtigen Funktionsaufrufe sehen, gibt es noch einige andere Möglichkeiten:
- Der Dienst wird ausgeführt oder heruntergefahren. Das bedeutet, dass die Stacks zu spät erfasst werden. In diesem Fall können Sie die ANR als falsch positiv ignorieren.
- Eine andere App-Komponente wird ausgeführt, z. B. ein Übertragungsempfänger. In diesem Fall ist der Hauptthread wahrscheinlich in dieser Komponente blockiert, sodass der Dienst nicht gestartet werden kann.
Wenn Sie einen wichtigen Funktionsaufruf sehen und feststellen können, wo der ANR-Fehler auftritt, prüfen Sie die restlichen Stacks des Hauptthreads, um den langsamen Vorgang zu finden und ihn zu optimieren oder vom kritischen Pfad zu entfernen.
- Achten Sie darauf, dass die App schnell startet, da dies in das ANR-Zeitlimit einbezogen wird, wenn die App zum Ausführen des Contentanbieters gestartet wird.
- Die Anfragen an den Contentanbieter müssen schnell sein.
- Führen Sie nicht viele gleichzeitige Binder-Aufrufe zum Blockieren aus, die alle Binder-Threads der App blockieren können.
- Stille UI-Freezes: Die Benutzeroberfläche der App reagiert nicht mehr auf Nutzereingaben.
- Abstürze: Die App stürzt möglicherweise mit einem
illegalStateExceptionab. - ANR-Fehler: Das System kann eine ANR-Zeitüberschreitung auslösen.
- Durchläufe planen:
ViewRootImplplant einen Durchlauf – einen Layout- und Zeichenvorgang –, indem eine Synchronisierungsbarriere für denMessageQueuedes UI-Threads gepostet wird. Durch diese Barriere wird die normale Nachrichtenverarbeitung unterbrochen, damit das UI-Layout Priorität hat. - Race Condition: Wenn mehrere Threads gleichzeitig versuchen, eine Ansicht ungültig zu machen, konkurrieren sie darum, diesen Durchlauf zu planen. Beide Threads können eine Synchronisationsbarriere einfügen, aber das Framework speichert nur das Token für einen der beiden.
- UI Freeze: Wenn die Traversierung ausgeführt wird, wird nur die einzelne gespeicherte Barriere entfernt. Die sekundären, „durchgesickerten“ Barrieren bleiben unbegrenzt in der Warteschlange und blockieren den UI-Thread dauerhaft für die Verarbeitung synchroner Nachrichten.
- Interagieren Sie nur mit
View-Objekten, einschließlich des Lesens von Attributen wiewidthoderheightoder des Festlegens von Attributen wieTextView's text, in dem Thread, in dem Sie die Ansichtshierarchie erstellt haben. Dies ist fast immer der Haupt- oder UI-Thread. - Wenn Sie nicht garantieren können, dass Sie beim Zugriff auf Ansichten im UI-Thread ausgeführt werden, gehen Sie davon aus, dass dies nicht sicher ist. Wenn Sie beispielsweise
Coroutinesfür Hintergrundarbeiten verwenden, müssen Sie zum Haupt-Dispatcher wechseln, bevor SieView-Objekte bearbeiten. ContextCompat#getMainExecutor(android.content.Context)View.post(Runnable)Activity.runOnUiThread(Runnable)- Installieren Sie eine debugfähige Version Ihrer App auf einem Gerät mit Android 17 oder höher.
- Öffnen Sie auf Ihrem Gerät die Einstellungen und gehen Sie zu System > Erweitert > Entwickleroptionen > Änderungen bei der App-Kompatibilität.
- Wählen Sie Ihre App aus der Liste aus.
- Suchen Sie in der Liste der Änderungen nach dem Schalter
ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APISund aktivieren Sie ihn. - Telemetrie und Tracking: Mit diesem Listener kann Ihre App Callbacks empfangen, wenn eine View API über einen falschen Thread aufgerufen wird. So lassen sich Telemetriedaten einfacher protokollieren und Fehler leichter finden.
- Inline-Ausführung: Der Listener wird inline aufgerufen, sodass Sie den genauen Stacktrace zum Zeitpunkt des Verstoßes erfassen und prüfen können.
- Systemweites Problem: Der Prozess wurde aufgrund einer hohen Systemlast oder eines Problems auf dem Systemserver nicht geplant.
- Late Stack Dump Der Thread wurde in dem kurzen Zeitraum zwischen dem Auslösen des ANR und dem Speichern der Stacks wiederhergestellt. Die Latenz auf Pixel-Geräten mit Android 13 beträgt etwa 100 ms, kann aber auch mehr als 1 Sekunde betragen. Die Latenz auf Pixel-Geräten mit Android 14 liegt in der Regel unter 10 ms.
- Falsche Zuordnung von Threads: Der Thread, der zum Erstellen der ANR-Signatur verwendet wurde, war nicht der tatsächliche Thread, der nicht reagiert hat und den ANR-Fehler verursacht hat.
- Verstoß gegen die Richtlinien für Threads ansehen: Wenn Ihre App eine Ansicht in einem Hintergrund-Thread ändert, kann dies zu einer Race-Bedingung in den Interna der Ansicht führen, wodurch die Ausführung von UI-Thread-Aufgaben blockiert wird.
- Hohe Systemlast:Untersuchen Sie den Gesamtdruck auf die Geräteressourcen, z. B. CPU-, Arbeitsspeicher- oder E/A-Engpässe im gesamten System, als primäre Ursache für die Reaktionslosigkeit.
- Falsche Zuordnung von Threads:Prüfen Sie Worker- und Binder-Threads auf Deadlocks, Konflikte bei der Sperrung, die den Hauptthread beeinträchtigen, oder hängende Hintergrundthreads, die asynchrone Komponenten (z.B.
goAsync()) verarbeiten. - Threading-Verstöße ansehen:Hintergrund-Threads werden auf unzulässige Änderungen an der UI-View-Hierarchie gescannt, die eine Synchronisationsbarriere in der MessageQueue zurücklassen und alle synchronen Nachrichten dauerhaft blockieren können.
- Das Erstellen des Stacks dauert zu lange und es kommt zu einem Zeitüberschreitungsfehler.
- Der Prozess wurde beendet oder beendet, bevor die Stacks erfasst wurden.
Weitere Informationen zu Diensten finden Sie auf den folgenden Seiten:
Contentanbieter reagiert nicht
Ein ANR-Fehler bei einem Contentanbieter tritt auf, wenn ein Remote-Contentanbieter länger als das Zeitlimit für die Antwort auf eine Anfrage benötigt und beendet wird.
Standardmäßiges Zeitlimit: wird vom Contentanbieter mit ContentProviderClient.setDetectNotResponding angegeben. Das ANR-Zeitlimit umfasst die Gesamtzeit für die Ausführung einer Anfrage an einen Remote-Contentanbieter, einschließlich des Kaltstarts der Remote-App, falls sie noch nicht ausgeführt wurde.
So vermeiden Sie ANRs bei Inhaltsanbietern:
Häufige Ursachen
In der folgenden Tabelle sind häufige Ursachen für ANRs von Inhaltsanbietern und entsprechende Korrekturen aufgeführt.
| Ursache | Was passiert? | Signal | Korrekturvorschlag |
|---|---|---|---|
| Langsame Abfrage des Contentanbieters | Die Ausführung durch den Inhaltsanbieter dauert zu lange oder ist blockiert. | Der android.content.ContentProvider$Transport.query-Frame befindet sich im Binder-Thread. |
Abfrage des Contentanbieters optimieren Ermitteln Sie, was den Binder-Thread blockiert. |
| Langsamer App-Start | Die App des Content-Anbieters braucht zu lange, um zu starten. | Der ActivityThread.handleBindApplication-Frame befindet sich im Hauptthread. |
App-Start optimieren |
| Erschöpfung der Binder-Threads – alle Binder-Threads sind beschäftigt | Alle Binder-Threads sind mit der Bearbeitung anderer synchroner Anfragen beschäftigt, sodass der Contentanbieter-Binder-Aufruf nicht ausgeführt werden kann. | Die App wird nicht gestartet, alle Binder-Threads sind ausgelastet und der Contentanbieter wird nicht ausgeführt. | Reduzieren Sie die Belastung der Binder-Threads. Das bedeutet, dass weniger synchrone ausgehende Binder-Aufrufe erfolgen oder weniger Arbeit bei der Verarbeitung eingehender Anrufe anfällt. |
Fehlerbehebung
Wenn Sie einen ANR-Fehler bei einem Contentanbieter mithilfe der Clustersignatur und des ANR-Berichts in der Google Play Console oder Firebase Crashlytics beheben möchten, sehen Sie sich an, was der Hauptthread und die Binder-Threads tun.
Das folgende Flussdiagramm beschreibt, wie Sie einen ANR-Fehler bei einem Contentanbieter beheben:
Das folgende Code-Snippet zeigt, wie der Binder-Thread aussieht, wenn er aufgrund einer langsamen Contentanbieter-Abfrage blockiert wird. In diesem Fall wartet die Anfrage des Inhaltsanbieters beim Öffnen einer Datenbank auf eine Sperre.
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)
Das folgende Code-Snippet zeigt, wie der Hauptthread aussieht, wenn er aufgrund eines langsamen App-Starts blockiert wird. In diesem Fall ist der App-Start langsam, weil es während der Dagger-Initialisierung zu Konflikten bei der Sperre kommt.
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)
Langsame Antwort auf Jobanfrage
Ein ANR-Fehler aufgrund einer langsamen Jobantwort tritt auf, wenn die App zu lange braucht, um auf JobService.onStartJob() oder JobService.onStopJob() zu reagieren, oder zu lange braucht, um eine Benachrichtigung mit JobService.setNotification() zu senden. Das deutet darauf hin, dass der Hauptthread der App durch eine andere Aufgabe blockiert wird.
Wenn es sich um ein Problem mit JobService.onStartJob() oder JobService.onStopJob() handelt, sehen Sie nach, was im Haupt-Thread passiert. Wenn es sich um ein Problem mit JobService.setNotification() handelt, rufen Sie so schnell wie möglich an.
Führen Sie nicht viele Aktionen aus, bevor Sie die Benachrichtigung anzeigen.
Verstoß gegen die Threading-Regeln ansehen
Wenn Ihre App eine Ansicht in einem Hintergrund-Thread ändert, kann dies einen Race Condition im internen Status der Ansichtshierarchie auslösen. Durch diesen Verstoß kann der Haupt-Thread (UI) daran gehindert werden, synchrone Nachrichten auszuführen, was zu schwerwiegenden Stabilitätsproblemen führen kann:
Dieser Threading-Verstoß ist eine der Hauptursachen für ANR-Stacktraces, bei denen der Hauptthread inaktiv zu sein scheint (z.B. in „nativePollOnce“ oder „main thread idle“ erfasst).
Leck in der Synchronisierungsbarriere
Ansichten können entweder indirekt durch Ändern ihres Status (z. B. durch Aufrufen von TextView.setText oder View.setVisibility) oder direkt durch Aufrufen von View.invalidate oder View.requestLayout ungültig gemacht werden. Wenn eine Ansicht ungültig wird, plant ViewRootImpl einen Durchlauf, um Messungen, Layout und Zeichnen durchzuführen und den UI-Status zu aktualisieren.
Infolgedessen friert die Benutzeroberfläche ein. Da MessageQueue keine synchronen Nachrichten nach den offengelegten Barrieren verarbeiten kann, wechselt der Hauptthread in den Leerlauf. Dieses Problem tritt in der Regel als ANR mit nativePollOnce im Stacktrace auf.
So vermeiden Sie Verstöße gegen die View-Threading-Regeln:
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { val data = myRepository.getData() withContext(Dispatchers.Main) { // Switch context to Main myTextView.text = data.title } }
Damit Sie Best Practices einhalten können, bietet Android verschiedene Möglichkeiten, von anderen Threads aus auf den UI-Thread zuzugreifen:
Beliebte Bild-Loader, Frameworks für reaktive Erweiterungen, Event-Bus-Bibliotheken und andere Threading-Lösungen bieten in der Regel Möglichkeiten, bestimmte Ereignisse im Haupt-Thread zu beobachten. Das ist nützlich für Beobachter und Event-Listener, die den Status von Ansichten abrufen und festlegen müssen.
Fehlerbehebung
Mit Android 17 werden neue Tools eingeführt, mit denen Sie während der Entwicklung View-Threading-Verstöße erkennen, nachverfolgen und beheben können.
1. Tools für das Kompatibilitäts-Framework
Mit den Tools des Kompatibilitäts-Frameworks können App-Entwickler Verhaltensänderungen einzeln über Entwickleroptionen oder ADB aktivieren und deaktivieren. So können Sie das Verhalten bei Verstößen gegen die Threading-Regeln ändern:
Sie können das Flag auch mit ADB aktivieren oder deaktivieren:
$ 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>
Wenn Sie dieses Flag aktivieren, wird die App gezwungen, eine Ausnahme auszulösen und abzustürzen, wenn eine View API vom falschen Thread aufgerufen wird. So können Sie Probleme mit View-Threading-Verstößen leichter erkennen und beheben.
2. CalledFromWrongThreadListener API
Sie können auch die globale View#registerCalledFromWrongThreadListener API implementieren, um unzulässige Threadzugriffe programmatisch zu erkennen.
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
Wenn Sie in den ANR-Stacks den Frame „nativePollOnce“ oder „message queue idle“ sehen, deutet das oft darauf hin, dass der mutmaßlich nicht reagierende Thread tatsächlich im Leerlauf war und auf Looper-Nachrichten gewartet hat. In der Google Play Console sehen die ANR-Details so aus:
Native method - android.os.MessageQueue.nativePollOnce
Executing service com.example.app/com.example.app.MyService
Wenn der Hauptthread beispielsweise inaktiv ist, sehen die Stacks so aus:
"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)
Es gibt mehrere Gründe, warum ein mutmaßlich nicht reagierender Thread inaktiv sein kann:
Da die zugrunde liegenden Trigger eines „nativePollOnce“-ANR je nach ANR-Typ variieren, kann die Diagnose der spezifischen ANR-Kategorie helfen, um umsetzbare Schritte zur Behebung des Problems in Ihrer App zu ermitteln.
| Kategorie | Ursache | Korrekturvorschlag |
|---|---|---|
| Zeitlimit für die Eingabeübermittlung | Systemweites Problem Stack-Dump zu spät |
Es sind keine weiteren Schritte erforderlich. |
| Kein fokussiertes Fenster | Systemweites Problem Später Stack-Dump Threading-Verstoß ansehen |
Prüfen Sie die Codebasis auf Verstöße gegen die View-Threading-Regeln. |
| Zeitüberschreitung des Übertragungsempfängers | Später Stack-Dump Systemweites Problem Falsche Zuordnung von Threads Threading-Verstoß ansehen |
Sehen Sie sich die relevanten Threads im Stackdump an. Prüfen Sie die Codebasis auf Verstöße gegen die View-Threading-Regeln. |
| Zeitüberschreitung bei der Ausführung des Dienstes | Später Stack-Dump Systemweites Problem Threading-Verstoß ansehen |
Prüfen Sie die Codebasis auf Verstöße gegen die View-Threading-Regeln. |
| Contentanbieter reagiert nicht | Später Stack-Dump Systemweites Problem Falsche Zuordnung von Threads |
Sehen Sie sich die relevanten Threads im Stackdump an. |
So analysieren Sie ANRs vom Typ „nativePollOnce“:
Keine Stapelframes
Einige ANR-Berichte enthalten nicht die Stacks mit dem ANR. Das bedeutet, dass das Stack-Dumping beim Generieren des ANR-Berichts fehlgeschlagen ist. Es gibt mehrere mögliche Gründe für fehlende Stackframes:
[...]
--- 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 -----
[...]
ANRs ohne Stackframes können nicht anhand der Clustersignatur oder des ANR-Berichts behoben werden. Sehen Sie sich zur Fehlerbehebung andere Cluster für die App an. Wenn ein Problem groß genug ist, hat es in der Regel einen eigenen Cluster, in dem Stackframes vorhanden sind. Eine weitere Option sind Perfetto-Traces.
Bekannte Probleme
Einen Timer im Prozess Ihrer App zu verwenden, um die Broadcast-Verarbeitung abzuschließen, bevor ein ANR ausgelöst wird, funktioniert möglicherweise nicht richtig, da das System ANRs asynchron überwacht.