Konzepte und Jetpack Compose-Implementierung
Wenn der UI-Thread einer Android-App zu lange blockiert ist, wird ein ANR-Fehler (Application Not Responding) ausgelöst. Wenn sich die App im Vordergrund befindet, wird dem Nutzer ein Dialogfeld angezeigt, wie in Abbildung 1 dargestellt. Im ANR-Dialogfeld kann der Nutzer die App beenden.
ANR-Fehler sind ein Problem, weil der Hauptthread der App, der für die Aktualisierung der Benutzeroberfläche verantwortlich ist, keine Nutzereingabeereignisse verarbeiten oder zeichnen kann, was zu Frustration beim Nutzer führt. Weitere Informationen zum Hauptthread der App finden Sie unter Prozesse und Threads – Übersicht.
Ein ANR-Fehler wird für Ihre App ausgelöst, wenn eine der folgenden Bedingungen eintritt:
- Zeitüberschreitung bei der Eingabeübertragung: Wenn Ihre App nicht innerhalb von 5 Sekunden auf ein Eingabe ereignis (z. B. einen Tastendruck oder eine Bildschirmberührung) reagiert hat.
- Ausführung des Dienstes: Wenn ein von Ihrer App deklarierter Dienst die Ausführung von
Service.onCreate()undService.onStartCommand()/Service.onBind()nicht innerhalb weniger Sekunden abschließen kann. **Service.startForeground()nicht aufgerufen**: Wenn Ihre AppContext.startForegroundService()verwendet, um einen neuen Dienst im Vordergrund zu starten der Dienst aberstartForeground()nicht innerhalb von 5 Sekunden aufruft.- Übertragung von Intents: Wenn die Ausführung eines
BroadcastReceivernicht innerhalb eines bestimmten Zeitraums abgeschlossen wurde. Wenn die App eine Aktivität im Vordergrund hat, beträgt dieses Zeitlimit 5 Sekunden. **JobSchedulerInteraktionen**: WennJobServicenicht innerhalb weniger Sekunden vonJobService.onStartJob()oderJobService.onStopJob()zurückgegeben wird oder wenn ein vom Nutzer initiierter Job gestartet wird und Ihre AppJobService.setNotification()nicht innerhalb weniger Sekunden nach dem Aufruf vonJobService.onStartJob()aufruft. Bei Apps, die auf Android 13 und niedriger ausgerichtet sind, werden ANR-Fehler nicht gemeldet. Bei Apps, die auf Android 14 und höher ausgerichtet sind, werden ANR-Fehler explizit gemeldet.
Wenn in Ihrer App ANR-Fehler auftreten, können Sie das Problem mithilfe der Informationen in diesem Artikel diagnostizieren und beheben.
Probleme beheben
Nachdem Sie das Problem identifiziert haben, können Sie die Tipps in diesem Abschnitt verwenden, um häufig auftretende Probleme zu beheben.
Langsamer Code im Hauptthread
Ermitteln Sie die Stellen in Ihrem Code, an denen der Hauptthread der App länger als 5 Sekunden beschäftigt ist. Suchen Sie in Ihrer App nach verdächtigen Anwendungsfällen und versuchen Sie, den ANR-Fehler zu reproduzieren.
Abbildung 2 zeigt beispielsweise eine Traceview-Zeitachse, auf der der Hauptthread länger als 5 Sekunden beschäftigt ist.

Abbildung 2 Traceview-Zeitachse, die einen beschäftigten Hauptthread zeigt
Abbildung 2 zeigt, dass der Großteil des fehlerhaften Codes im
onClick(View) Handler enthalten ist, wie im folgenden Codebeispiel dargestellt:
Kotlin
override fun onClick(v: View) {
// This task runs on the main thread.
BubbleSort.sort(data)
}
Java
@Override
public void onClick(View view) {
// This task runs on the main thread.
BubbleSort.sort(data);
}
In diesem Fall sollten Sie die Arbeit, die im Hauptthread ausgeführt wird, in einen Worker-Thread verschieben. Das Android-Framework enthält Klassen, mit denen Sie die Aufgabe in einen Arbeitsthread verschieben können. Weitere Informationen finden Sie unter Worker-Threads.
E/A im Hauptthread
Die Ausführung von E/A-Vorgängen im Hauptthread ist eine häufige Ursache für langsame Vorgänge im Hauptthread, die zu ANR-Fehlern führen können. In Compose lösen Entwickler oft versehentlich Festplattenlesevorgänge aus (z. B. SharedPreferences oder Datenbankaufrufe), während sie versuchen, den Anfangszustand abzuleiten.
Führen Sie E/A-Vorgänge mit langer Ausführungszeit außerhalb der UI-Ebene aus. Verwenden Sie
withContext(Dispatchers.IO) in einem ViewModel oder noch besser ein Repository
auf der Datenebene. Es wird empfohlen, alle E/A-Vorgänge in einen Worker-Thread zu verschieben, wie im vorherigen Abschnitt beschrieben.
Beispiele für E/A-Vorgänge sind Netzwerk- und Speichervorgänge. Weitere Informationen finden Sie unter Netzwerkvorgänge ausführen und Daten speichern.
Sperrenkonflikt
In einigen Fällen wird die Arbeit, die den ANR-Fehler verursacht, nicht direkt im Hauptthread der App ausgeführt. Wenn ein Arbeitsthread eine Sperre für eine Ressource hält, die der Hauptthread zum Abschließen seiner Arbeit benötigt, kann ein ANR-Fehler auftreten.
Abbildung 3 zeigt beispielsweise eine Traceview-Zeitachse, auf der der Großteil der Arbeit in einem Arbeitsthread ausgeführt wird.

Abbildung 3 Traceview-Zeitachse, die die Arbeit zeigt, die in einem Worker-Thread ausgeführt wird
Wenn bei Ihren Nutzern weiterhin ANR-Fehler auftreten, sollten Sie den Status des Hauptthreads im Android Device Monitor prüfen. Normalerweise befindet sich der Hauptthread im
RUNNABLE Status, wenn er bereit ist, die Benutzeroberfläche zu aktualisieren, und im Allgemeinen
reagiert.
Wenn die Ausführung des Hauptthreads jedoch nicht fortgesetzt werden kann, befindet er sich im BLOCKED
Status und kann nicht auf Ereignisse reagieren. Der Status wird im Android Device Monitor als
Monitor oder Wait angezeigt, wie in Abbildung 5 dargestellt.

Abbildung 4 Hauptthread im Status „Monitor“
Der folgende Trace zeigt den Hauptthread einer App, der blockiert ist und auf eine Ressource wartet:
...
AsyncTask #2" prio=5 tid=18 Runnable
| group="main" sCount=0 dsCount=0 obj=0x12c333a0 self=0x94c87100
| sysTid=25287 nice=10 cgrp=default sched=0/0 handle=0x94b80920
| state=R schedstat=( 0 0 0 ) utm=757 stm=0 core=3 HZ=100
| stack=0x94a7e000-0x94a80000 stackSize=1038KB
| held mutexes= "mutator lock"(shared held)
at com.android.developer.anrsample.BubbleSort.sort(BubbleSort.java:8)
at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:147)
- locked <0x083105ee> (a java.lang.Boolean)
at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:135)
at android.os.AsyncTask$2.call(AsyncTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
...
Wenn Sie den Trace überprüfen, können Sie den Code finden, der den Hauptthread blockiert. Der folgende Code ist für die Sperre verantwortlich, die den Hauptthread im vorherigen Trace blockiert:
Kotlin
override fun onClick(v: View) {
// The worker thread holds a lock on lockedResource
LockTask().execute(data)
synchronized(lockedResource) {
// The main thread requires lockedResource here
// but it has to wait until LockTask finishes using it.
}
}
class LockTask : AsyncTask<Array<Int>, Int, Long>() {
override fun doInBackground(vararg params: Array<Int>): Long? =
synchronized(lockedResource) {
// This is a long-running operation, which makes
// the lock last for a long time
BubbleSort.sort(params[0])
}
}
Java
@Override
public void onClick(View v) {
// The worker thread holds a lock on lockedResource
new LockTask().execute(data);
synchronized (lockedResource) {
// The main thread requires lockedResource here
// but it has to wait until LockTask finishes using it.
}
}
public class LockTask extends AsyncTask<Integer[], Integer, Long> {
@Override
protected Long doInBackground(Integer[]... params) {
synchronized (lockedResource) {
// This is a long-running operation, which makes
// the lock last for a long time
BubbleSort.sort(params[0]);
}
}
}
Ein weiteres Beispiel ist der Hauptthread einer App, der auf ein Ergebnis von einem Arbeitsthread wartet, wie im folgenden Code dargestellt. Die Verwendung von wait() und notify() ist in Kotlin nicht empfehlenswert, da Kotlin eigene Mechanismen für die Verarbeitung von Parallelität hat. Wenn Sie Kotlin verwenden, sollten Sie nach Möglichkeit Kotlin-spezifische Mechanismen verwenden.
Kotlin
fun onClick(v: View) {
val lock = java.lang.Object()
val waitTask = WaitTask(lock)
synchronized(lock) {
try {
waitTask.execute(data)
// Wait for this worker thread's notification
lock.wait()
} catch (e: InterruptedException) {
}
}
}
internal class WaitTask(private val lock: java.lang.Object) : AsyncTask<Array<Int>, Int, Long>() {
override fun doInBackground(vararg params: Array<Int>): Long? {
synchronized(lock) {
BubbleSort.sort(params[0])
// Finished, notify the main thread
lock.notify()
}
}
}
Java
public void onClick(View v) {
WaitTask waitTask = new WaitTask();
synchronized (waitTask) {
try {
waitTask.execute(data);
// Wait for this worker thread’s notification
waitTask.wait();
} catch (InterruptedException e) {}
}
}
class WaitTask extends AsyncTask<Integer[], Integer, Long> {
@Override
protected Long doInBackground(Integer[]... params) {
synchronized (this) {
BubbleSort.sort(params[0]);
// Finished, notify the main thread
notify();
}
}
}
Es gibt noch einige andere Situationen, die den Hauptthread blockieren können, darunter
Threads, die Lock, Semaphore sowie einen Ressourcenpool
(z. B. einen Pool von Datenbankverbindungen) oder andere Mechanismen zur gegenseitigen Ausschließung (Mutex)
verwenden.
Sie sollten die Sperren, die Ihre App für Ressourcen hält, im Allgemeinen prüfen. Wenn Sie jedoch ANR-Fehler vermeiden möchten, sollten Sie sich die Sperren für Ressourcen ansehen, die der Hauptthread benötigt.
Achten Sie darauf, dass die Sperren nur für die kürzestmögliche Zeit gehalten werden. Noch besser ist es, zu prüfen, ob die App die Sperre überhaupt benötigt. Wenn Sie die
Sperre verwenden, um zu bestimmen, wann die Benutzeroberfläche basierend auf der Verarbeitung eines Arbeitsthreads aktualisiert werden soll,
verwenden Sie Mechanismen wie onProgressUpdate() und onPostExecute() um
zwischen dem Arbeitsthread und dem Hauptthread zu kommunizieren.
Langsame Übertragungsempfänger
Apps können mithilfe von Übertragungsempfängern auf Übertragungsnachrichten reagieren, z. B. auf das Aktivieren oder Deaktivieren des Flugmodus oder auf eine Änderung des Verbindungsstatus. Ein ANR-Fehler tritt auf, wenn die Verarbeitung der Nachricht an alle durch eine App zu lange dauert.
Ein ANR-Fehler tritt in den folgenden Fällen auf:
- Ein Übertragungsempfänger hat die Ausführung seiner
onReceiveMethode nicht innerhalb eines angemessenen Zeitraums abgeschlossen. - Ein Übertragungsempfänger ruft
goAsyncauf und ruftfinishnicht für dasPendingResult-Objekt auf.
Ihre App sollte in der onReceive-Methode eines
BroadcastReceiver nur kurze Vorgänge ausführen. Wenn Ihre App jedoch eine komplexere Verarbeitung als Folge einer Nachricht an alle erfordert, sollten Sie die Aufgabe an ein ViewModel (mit den Vorteilen von Kotlin-Koroutinen, -Scopes und -Dispatchern) delegieren, wenn die Aufgabe voraussichtlich höchstens einige Sekunden dauert, an einen beliebigen Typ von State Holder, oder an WorkManager für Aufgaben, die voraussichtlich länger als einige Sekunden dauern.
Mit Tools wie Traceview können Sie ermitteln, ob Ihr Übertragungsempfänger Vorgänge mit langer Ausführungszeit im Hauptthread der App ausführt. Abbildung 6 zeigt beispielsweise die Zeitachse eines Übertragungsempfängers, der eine Nachricht im Hauptthread etwa 100 Sekunden lang verarbeitet.

Abbildung 5 Traceview-Zeitachse, die die Arbeit des BroadcastReceiver im Hauptthread zeigt
Dieses Verhalten kann durch die Ausführung von Vorgängen mit langer Ausführungszeit in der
onReceive() Methode des BroadcastReceiver verursacht werden, wie im
folgenden Beispiel dargestellt:
Kotlin
override fun onReceive(context: Context, intent: Intent) {
// This is a long-running operation
BubbleSort.sort(data)
}
Java
@Override
public void onReceive(Context context, Intent intent) {
// This is a long-running operation
BubbleSort.sort(data);
}
In solchen Fällen wird empfohlen, den Vorgang mit langer Ausführungszeit in ein IntentService zu verschieben, da dieser einen Arbeitsthread verwendet, um seine Arbeit auszuführen.
Der folgende Code zeigt, wie ein IntentService verwendet wird, um einen
Vorgang mit langer Ausführungszeit zu verarbeiten:
Kotlin
override fun onReceive(context: Context, intent: Intent) {
Intent(context, MyIntentService::class.java).also { intentService ->
// The task now runs on a worker thread.
context.startService(intentService)
}
}
class MyIntentService : IntentService("MyIntentService") {
override fun onHandleIntent(intent: Intent?) {
BubbleSort.sort(data)
}
}
Java
@Override
public void onReceive(Context context, Intent intent) {
// The task now runs on a worker thread.
Intent intentService = new Intent(context, MyIntentService.class);
context.startService(intentService);
}
public class MyIntentService extends IntentService {
@Override
protected void onHandleIntent(@Nullable Intent intent) {
BubbleSort.sort(data);
}
}
Durch die Verwendung des IntentService wird der Vorgang mit langer Ausführungszeit in einem Arbeitsthread anstelle des Hauptthreads ausgeführt. Abbildung 7 zeigt die Arbeit, die in der Traceview-Zeitachse an den Arbeitsthread delegiert wurde.

Abbildung 6 Traceview-Zeitachse, die die Übertragungsnachricht zeigt, die in einem Worker-Thread verarbeitet wird
Ihr Übertragungsempfänger kann goAsync() verwenden, um dem System zu signalisieren, dass es
mehr Zeit für die Verarbeitung der Nachricht benötigt. Sie sollten jedoch
finish() für das PendingResult-Objekt aufrufen. Das folgende Beispiel zeigt, wie finish() aufgerufen wird, damit das System den Übertragungsempfänger wiederverwenden und einen ANR-Fehler vermeiden kann:
Kotlin
val pendingResult = goAsync()
object : AsyncTask<Array<Int>, Int, Long>() {
override fun doInBackground(vararg params: Array<Int>): Long? {
// This is a long-running operation
BubbleSort.sort(params[0])
pendingResult.finish()
return 0L
}
}.execute(data)
Java
final PendingResult pendingResult = goAsync();
new AsyncTask<Integer[], Integer, Long>() {
@Override
protected Long doInBackground(Integer[]... params) {
// This is a long-running operation
BubbleSort.sort(params[0]);
pendingResult.finish();
}
}.execute(data);
Wenn Sie den Code von einem langsamen Übertragungsempfänger in einen anderen Thread verschieben und
verwenden goAsync(), wird der ANR-Fehler jedoch nicht behoben, wenn die Übertragung im Hintergrund erfolgt.
Das ANR-Zeitlimit gilt weiterhin.