Konzepte und Jetpack Compose-Implementierung
Wenn der UI-Thread einer Android-App zu lange blockiert ist, wird ein ANR-Fehler (App antwortet nicht) 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 das Beenden der App erzwingen.
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 erfüllt ist:
- Zeitüberschreitung bei der Eingabeverteilung: Wenn Ihre App innerhalb von 5 Sekunden nicht auf ein Eingabeereignis (z. B. Tastendruck oder Berührung des Bildschirms) reagiert hat.
- Dienst wird ausgeführt: Wenn ein von deiner App deklarierter Dienst
Service.onCreate()undService.onStartCommand()/Service.onBind()nicht innerhalb weniger Sekunden ausführen kann. **Service.startForeground()nicht aufgerufen**: Wenn deine AppContext.startForegroundService()verwendet, um einen neuen Dienst im Vordergrund zu starten, der Dienst aber nicht innerhalb von 5 SekundenstartForeground()aufruft.- Broadcast of Intent: Wenn ein
BroadcastReceivernicht innerhalb einer bestimmten Zeit ausgeführt wurde. Wenn die App im Vordergrund aktiv ist, beträgt dieses Zeitlimit 10 Sekunden. **JobScheduler-Interaktionen**: Wenn einJobServicenicht innerhalb weniger Sekunden vonJobService.onStartJob()oderJobService.onStopJob()zurückkehrt oder wenn ein vom Nutzer initiierter Job gestartet wird und Ihre App nicht innerhalb weniger Sekunden nach dem Aufruf vonJobService.onStartJob()die MethodeJobService.setNotification()aufruft. Bei Apps, die auf Android 13 und niedriger ausgerichtet sind, werden ANRs nicht gemeldet. Bei Apps, die auf Android 14 und höher ausgerichtet sind, werden ANRs explizit gemeldet.
Wenn in deiner App ANR-Fehler auftreten, kannst du die Anleitung in diesem Artikel verwenden, um das Problem zu diagnostizieren und zu 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 im 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.
In Abbildung 2 ist beispielsweise eine Traceview-Zeitachse zu sehen, auf der der Hauptthread länger als 5 Sekunden beschäftigt ist.

Abbildung 2. Traceview-Zeitachse mit einem ausgelasteten Hauptthread
Abbildung 2 zeigt, dass der Großteil des fehlerhaften Codes im Handler onClick(View) auftritt, wie im folgenden Codebeispiel zu sehen ist:
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 Arbeitsthread verschieben. Das Android-Framework enthält Klassen, mit denen die Aufgabe in einen Arbeitsthread verschoben werden kann. Weitere Informationen finden Sie unter Arbeitsthreads.
E/A im Hauptthread
Das Ausführen 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 Lesevorgänge auf dem Laufwerk aus (z. B. SharedPreferences oder Datenbankaufrufe), wenn sie versuchen, den Anfangszustand abzuleiten.
Führen Sie lang andauernde E/A-Vorgänge 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 Arbeitsthread zu verschieben, wie im vorherigen Abschnitt gezeigt.
Beispiele für E/A-Vorgänge sind Netzwerk- und Speichervorgänge. Weitere Informationen findest du 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 Ressource sperrt, 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 Arbeitsthread ausgeführt wird
Wenn bei deinen Nutzern jedoch weiterhin ANRs auftreten, solltest du dir den Status des Hauptthreads im Android Device Monitor ansehen. Normalerweise befindet sich der Hauptthread im Status RUNNABLE, wenn er bereit ist, die Benutzeroberfläche zu aktualisieren, und im Allgemeinen reagiert.
Wenn der Hauptthread die Ausführung jedoch nicht fortsetzen kann, befindet er sich im Status BLOCKED und kann nicht auf Ereignisse reagieren. Der Status wird im Android Device Monitor als Monitor oder Wait angezeigt, wie in Abbildung 5 zu sehen.

Abbildung 4. Hauptthread im Status „Monitor“
Der folgende Trace zeigt den Hauptthread einer App, der blockiert ist, weil er 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)
...
Anhand des Trace 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 gezeigt. Die Verwendung von wait() und notify() ist in Kotlin kein empfohlenes Muster, da Kotlin eigene Mechanismen für die Nebenläufigkeit 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 andere Situationen, in denen der Hauptthread blockiert werden kann, z. B. Threads, die Lock, Semaphore sowie einen Ressourcenpool (z. B. einen Pool von Datenbankverbindungen) oder andere Mechanismen zum gegenseitigen Ausschluss (Mutex) verwenden.
Du solltest die Sperren, die deine App für Ressourcen hält, im Allgemeinen prüfen. Wenn du jedoch ANRs vermeiden möchtest, solltest du dir die Sperren für Ressourcen ansehen, die vom Hauptthread benötigt werden.
Achten Sie darauf, dass die Sperren so kurz wie möglich gehalten werden, oder prüfen Sie, ob die App die Sperre überhaupt benötigt. Wenn Sie die Sperre verwenden, um festzulegen, 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 Broadcast-Nachrichten reagieren, z. B. auf das Aktivieren oder Deaktivieren des Flugmodus oder auf eine Änderung des Verbindungsstatus. Ein ANR-Fehler tritt auf, wenn eine App zu lange braucht, um die Broadcast-Nachricht zu verarbeiten.
Ein ANR-Fehler tritt in den folgenden Fällen auf:
- Ein Übertragungsempfänger hat die Methode
onReceivenicht innerhalb eines angemessenen Zeitraums ausgeführt. - Ein Übertragungsempfänger ruft
goAsyncauf und kannfinishnicht für dasPendingResult-Objekt aufrufen.
In der Methode onReceive eines BroadcastReceiver sollten nur kurze Vorgänge ausgeführt werden. Wenn Ihre App jedoch aufgrund einer Nachricht an alle eine komplexere Verarbeitung erfordert, sollten Sie die Aufgabe an einen ViewModel (unter Nutzung der Leistungsfähigkeit von Kotlin-Koroutinen, -Bereichen und -Dispatchern) delegieren, wenn die Aufgabe voraussichtlich höchstens ein paar Sekunden dauert, an einen beliebigen Typ von State Holder oder an WorkManager für Aufgaben, die voraussichtlich länger als ein paar Sekunden dauern.
Mit Tools wie Traceview können Sie feststellen, ob Ihr Übertragungsempfänger Vorgänge mit langer Ausführungszeit im Hauptthread der App ausführt. In Abbildung 6 ist beispielsweise die Zeitachse eines Übertragungsempfängers zu sehen, der eine Nachricht im Hauptthread etwa 100 Sekunden lang verarbeitet.

Abbildung 5. Traceview-Zeitachse mit der BroadcastReceiver-Arbeit im Hauptthread
Dieses Verhalten kann durch die Ausführung von Vorgängen mit langer Ausführungszeit für die Methode onReceive() des BroadcastReceiver verursacht werden, wie im folgenden Beispiel gezeigt:
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 Situationen empfiehlt es sich, den Vorgang mit langer Ausführungszeit in einen IntentService zu verschieben, da dieser einen Arbeitsthread verwendet, um seine Arbeit auszuführen.
Der folgende Code zeigt, wie Sie einen IntentService verwenden, 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 von IntentService wird der Vorgang mit langer Ausführungszeit in einem Arbeitsthread statt im Hauptthread ausgeführt. Abbildung 7 zeigt die Arbeit, die in der Traceview-Zeitachse an den Arbeitsthread zurückgestellt wurde.

Abbildung 6. Traceview-Zeitachse mit der Broadcast-Nachricht, die in einem Arbeitsthread verarbeitet wird
Dein Übertragungsempfänger kann goAsync() verwenden, um dem System zu signalisieren, dass er 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 recyceln 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 du den Code von einem langsamen Übertragungsempfänger in einen anderen Thread verschiebst und goAsync() verwendest, wird der ANR-Fehler jedoch nicht behoben, wenn die Übertragung im Hintergrund erfolgt.
Das ANR-Zeitlimit gilt weiterhin.