android.os.NetworkOnMainThreadException bedeutet, dass eine Android-Anwendung versucht hat, einen Netzwerkvorgang in ihrem UI-Hauptthread auszuführen. Android löst diese Ausnahme bei Anwendungen aus, deren Ziel-SDK Honeycomb beziehungsweise API-Level 11 oder höher ist. Die sicherste erste Maßnahme besteht darin, die Ausnahme nicht zu unterdrücken: Untersuchen Sie den fehlerhaften Aufrufpfad, ermitteln Sie den ersten anwendungseigenen blockierenden Netzwerkvorgang und prüfen Sie, ob die betroffene Client-API synchron oder bereits für Aufrufe aus dem Hauptthread geeignet ist.
Was android.os.NetworkOnMainThreadException bedeutet
Die genaue Ausnahme lautet android.os.NetworkOnMainThreadException. Laut der Android-Referenz zu NetworkOnMainThreadException wird sie ausgelöst, wenn eine Anwendung versucht, Netzwerkvorgänge in ihrem Hauptthread auszuführen. Sie wurde mit API-Level 11 eingeführt und wird bei Anwendungen ausgelöst, deren Ziel-SDK Honeycomb oder höher ist.
Auslöser können blockierende Arbeiten mit URL-Verbindungen, URL.openStream(), Socket-Zugriffe, Namensauflösungen oder synchrone Client-Aufrufe sein. Auch Koroutinen können die Ausnahme auslösen, wenn sie blockierende Ein-/Ausgabe ausführen, ohne zu einem geeigneten Hintergrund-Dispatcher zu wechseln. Die Kennzeichnung einer Funktion mit suspend verlagert diese Arbeit nicht automatisch aus dem Hauptthread.
Solange der Hauptthread blockiert ist, kann er keine UI-Ereignisse verarbeiten. Dadurch drohen eine verzögerte Reaktion der Benutzeroberfläche und der Zustand „Anwendung reagiert nicht“. Anwendungen mit einem älteren Ziel-SDK waren dieser Ausnahme nicht auf dieselbe Weise unterworfen. Netzwerkzugriffe im Hauptthread sind deshalb jedoch weder sicher noch empfehlenswert.
Blockierenden Netzwerkaufruf im fehlerhaften Pfad finden
Verwenden Sie den Stacktrace des reproduzierbaren Fehlers, um den synchronen Vorgang zu finden, bevor Sie das Ausführungsmodell ändern. Die genauen Frame-Namen und ihre Reihenfolge unterscheiden sich je nach Anwendung und Netzwerkbibliothek. Dieser Schritt muss daher im jeweiligen Projekt überprüft werden.
- Reproduzieren Sie den Vorgang, bei dem
android.os.NetworkOnMainThreadExceptiongemeldet wird, und sichern Sie den vollständigen Stacktrace. - Lesen Sie den Stacktrace von der Ausnahme in Richtung der Anwendungs-Frames. Suchen Sie nach dem ersten Frame aus Ihrem Projekt, der zu Arbeiten mit URL-Verbindungen,
URL.openStream(), Socket-Zugriffen, Namensauflösungen oder einem synchronen Client-Aufruf führt. - Prüfen Sie in der Dokumentation der betroffenen Bibliothek, ob die Methode blockierend, Callback-basiert oder eine dokumentierte, für den Hauptthread geeignete Suspend-API ist.
- Verfolgen Sie den Ausführungskontext des Aufrufers. Aufrufpfade aus Activities, Fragments, Composable-Callbacks und
viewModelScopebeginnen häufig im Hauptthread. Der tatsächliche Pfad muss jedoch im jeweiligen Projekt überprüft werden. - Wählen Sie eine der unten aufgeführten, passenden Korrekturen, anstatt jede Netzwerk-API zusätzlich in einen weiteren Ausführungsmechanismus einzuschließen.
In einem Stacktrace kann StrictMode$AndroidBlockGuardPolicy.onNetwork oberhalb eines anwendungseigenen Frames erscheinen. Das ist ein häufiges Diagnosemuster, aber keine universell gültige Frame-Reihenfolge. Entscheidend ist der projektinterne Aufruf, der den blockierenden Vorgang gestartet hat.
StrictMode nur für zusätzliche Diagnosen beim Debuggen verwenden
Wann dies geeignet ist: Verwenden Sie StrictMode bei lokalen Tests, wenn der vorhandene Fehler einen unbeabsichtigten Netzwerkzugriff im Hauptthread nicht eindeutig erkennen lässt.
Voraussetzungen: Verwenden Sie einen Debug-Build oder eine lokale Testumgebung. Planen Sie außerdem ein, die Diagnoserichtlinie nach der Überprüfung zu entfernen oder zu lockern.
- Aktivieren Sie StrictMode im Debug-Build.
- Lösen Sie den fehlerhaften Ablauf aus.
- Untersuchen Sie den gemeldeten Verstoß und ermitteln Sie den ersten anwendungseigenen blockierenden Netzwerkaufruf.
- Korrigieren Sie die Thread-Zuordnung des Aufrufs, anstatt den Verstoß zu unterdrücken.
Erwartetes Ergebnis: Die Diagnose zeigt einen anwendungseigenen Pfad, dem die richtige asynchrone API beziehungsweise der richtige Dispatcher oder Executor zugewiesen werden kann.
Risiko und Rücknahme: Das Risiko ist gering; ein Datenverlust ist nicht zu erwarten. Deaktivieren Sie die ausschließlich für das Debugging vorgesehene Richtlinie, nachdem die Korrektur überprüft wurde. Verwenden Sie permitAll() oder permitNetwork() nicht als Korrektur für die Produktionsversion.
Die zur Netzwerk-API passende Korrektur auswählen
Verwenden Sie den am wenigsten invasiven geeigneten Weg. Stellen Sie zunächst fest, ob der Client die asynchrone Ausführung bereits selbst übernimmt. Fügen Sie nur dann einen Dispatcher oder Executor hinzu, wenn der zugrunde liegende Vorgang blockierend ist.
Korrektur 1: Eine bereits asynchrone oder für den Hauptthread geeignete Client-API direkt verwenden
Wann dies geeignet ist: Verwenden Sie diesen Weg, wenn die Bibliothek dokumentiert, dass ihre Callback- oder Suspend-API die Hintergrundausführung bereits übernimmt und sicher aus Code im Hauptthread aufgerufen werden kann.
Voraussetzungen: Bestätigen Sie dieses Verhalten anhand der Dokumentation der Client-Bibliothek. Gehen Sie nicht davon aus, dass jede mit suspend gekennzeichnete Methode für den Hauptthread geeignet ist.
- Bevorzugen Sie die dokumentierte asynchrone Callback-API oder eine für den Hauptthread geeignete Suspend-API der Bibliothek.
- Rufen Sie diese API aus Code auf, der sicher im Hauptthread ausgeführt werden kann.
- Schließen Sie eine API, die laut Dokumentation bereits für den Hauptthread geeignet ist, nicht zusätzlich und unnötig in
withContext(Dispatchers.IO)ein. - Setzen Sie die UI-Arbeit nach Abschluss des Vorgangs im Hauptthread fort.
Erwartetes Ergebnis: Die Bibliothek führt den Netzwerkvorgang aus, ohne den Android-Hauptthread zu blockieren, während der Aufrufer das Ergebnis im richtigen UI-Kontext verarbeitet.
Risiko und Rücknahme: Das Risiko ist gering; ein Datenverlust ist nicht zu erwarten. Falls die Dokumentation oder Tests zeigen, dass die API doch nicht für den Hauptthread geeignet ist, verwenden Sie diesen Weg nicht weiter und verlagern Sie stattdessen die blockierende Ebene aus dem Hauptthread. Die Dokumentation zu Kotlin-Koroutinen unter Android unterscheidet zwischen blockierendem Code, für den Dispatchers.IO erforderlich ist, und Netzwerk-APIs, die bereits ein für den Hauptthread geeignetes Suspend-Verhalten bereitstellen.
Korrektur 2: Blockierende Kotlin-Netzwerkarbeit mit withContext(Dispatchers.IO) verlagern
Wann dies geeignet ist: Verwenden Sie diesen Weg für eine Kotlin-Suspend-Funktion, die blockierenden Netzwerk- oder Namensauflösungscode direkt aufruft.
Voraussetzungen: Sie müssen die Suspend-Funktion oder Repository-Ebene kontrollieren. Außerdem muss der zugrunde liegende Vorgang blockierend sein und darf nicht bereits für den Hauptthread geeignet sein.
- Lassen Sie den Aufrufer im Hauptthread, wenn er für den UI-Zustand verantwortlich ist.
- Schließen Sie nur den blockierenden Netzwerkblock in
withContext(Dispatchers.IO)ein oder verwenden Sie den im Projekt injizierten I/O-Dispatcher. - Geben Sie ausschließlich das Ergebnis des Vorgangs an den Aufrufer zurück.
- Aktualisieren Sie den UI-Zustand nach Abschluss des Suspend-Aufrufs wieder im Hauptthread.
Erwartetes Ergebnis: Blockierende Ein-/Ausgabe wird auf dem I/O-Dispatcher ausgeführt, während der UI-Zustand weiterhin dem Hauptthread zugeordnet bleibt.
Risiko und Rücknahme: Das Risiko ist gering; ein Datenverlust ist nicht zu erwarten. Entfernen Sie den Dispatcher-Wechsel nur, wenn bestätigt wurde, dass die Bibliothek eine für den Hauptthread geeignete Suspend-API bereitstellt. Die Best Practices für Koroutinen unter Android verlangen, dass Suspend-Funktionen, die aus dem Hauptthread aufgerufen werden können, für diesen geeignet sind. Dort wird außerdem darauf hingewiesen, dass viewModelScope normalerweise auf Dispatchers.Main beginnt.
Korrektur 3: Einen Java-Executor verwenden und das Ergebnis an den Hauptthread zurückgeben
Wann dies geeignet ist: Verwenden Sie diesen Weg für Java-Code, der blockierende Netzwerk-Ein-/Ausgabe direkt ausführt.
Voraussetzungen: Die Anwendung muss über einen im Hintergrund ausgeführten Executor oder Threadpool sowie einen Callback-Pfad zum Hauptthread für UI-Aktualisierungen verfügen.
- Übergeben Sie die blockierende Netzwerkaufgabe an den Executor.
- Führen Sie den Netzwerkaufruf innerhalb dieser Hintergrundaufgabe und nicht im Hauptthread aus.
- Geben Sie das Ergebnis über den Callback-Pfad der Anwendung an den Hauptthread zurück.
- Aktualisieren Sie Views ausschließlich innerhalb dieses Callbacks im Hauptthread.
Erwartetes Ergebnis: Der blockierende Vorgang wird über den Executor ausgeführt. Nur die daraus folgende UI-Arbeit kehrt in den Hauptthread zurück.
Risiko und Rücknahme: Das Risiko ist gering; ein Datenverlust ist nicht zu erwarten. Brechen Sie die Aufgabe ab oder übermitteln Sie keine neue Arbeit mehr, wenn die auslösende Benutzeroberfläche nicht mehr vorhanden ist. Durch das Verlagern des Netzwerkvorgangs aus dem Hauptthread wird es nicht zulässig, Views aus der Hintergrundaufgabe zu aktualisieren.
Lebenszyklus und dauerhafte Arbeit berücksichtigen, ohne einen weiteren Fehler zu verursachen
Eine korrekte Thread-Zuordnung macht eine Anfrage nicht automatisch lebenszyklussicher. Eine Anfrage kann länger als ihre Activity oder ihr Fragment bestehen, ein Ergebnis an eine nicht mehr aktuelle Benutzeroberfläche zurückgeben oder nach wiederholter Auslösung mehrfach gestartet werden. Abbruchverhalten, Behandlung der Zerstörung und Regeln für doppelte Anfragen hängen von der Architektur der Anwendung ab und müssen im Zielprojekt überprüft werden.
- Prüfen Sie, was geschieht, wenn die Activity oder das Fragment zerstört wird, während die Arbeit noch aktiv ist.
- Stellen Sie sicher, dass Hintergrundarbeit keine Views aktualisiert, nachdem die zugehörige Benutzeroberfläche nicht mehr vorhanden ist.
- Prüfen Sie, ob wiederholte Aktionen doppelte Anfragen auslösen können.
- Brechen Sie die Arbeit ab oder ignorieren Sie ihr Ergebnis, wenn der zugehörige Lebenszyklus es nicht mehr benötigt.
WorkManager nur für dauerhafte oder aufschiebbare Arbeit verwenden
Wann dies geeignet ist: Verwenden Sie WorkManager, wenn die Netzwerkaufgabe aufschiebbar oder lang andauernd ist oder Neustarts der Anwendung überstehen soll. WorkManager ist nicht der standardmäßige Ersatz für jede unmittelbar durch die Benutzeroberfläche ausgelöste Anfrage.
Voraussetzungen: Der Vorgang muss sich als Hintergrundarbeit abbilden lassen und darf keine sofortige Anfrage sein, die an den aktuellen Bildschirm gebunden ist.
- Erstellen Sie einen WorkRequest oder Worker für den Hintergrundvorgang.
- Fügen Sie Netzwerkeinschränkungen hinzu, wenn die Aufgabe sie benötigt.
- Lassen Sie WorkManager den Vorgang außerhalb des UI-Threads ausführen.
- Beobachten Sie den Abschluss und aktualisieren Sie die Benutzeroberfläche getrennt davon.
Erwartetes Ergebnis: Dauerhafte oder aufschiebbare Arbeit wird unabhängig vom auslösenden Bildschirm verwaltet und blockiert den Hauptthread nicht.
Risiko und Rücknahme: Das Risiko ist gering; ein Datenverlust ist nicht zu erwarten. Brechen Sie den WorkRequest ab, wenn die Aufgabe nicht mehr benötigt wird. Falls die Anfrage eine sofortige Antwort für die aktuelle Benutzeroberfläche erfordert, verwenden Sie stattdessen die geeignete asynchrone API, Koroutine oder den Executor-Weg.
Prüfen, ob die Ausnahme behoben ist
- Reproduzieren Sie den ursprünglich fehlerhaften Ablauf, nachdem Sie einen passenden Reparaturweg umgesetzt haben.
- Bestätigen Sie, dass der blockierende Netzwerk- oder Namensauflösungsvorgang nicht mehr im Hauptthread ausgeführt wird.
- Bestätigen Sie, dass Aktualisierungen von Views und UI-Zustand erst erfolgen, nachdem das Ergebnis in den Hauptthread zurückgekehrt ist.
- Testen Sie, was geschieht, wenn die auslösende Activity oder das Fragment vor Abschluss des Vorgangs zerstört wird.
- Testen Sie wiederholte Eingaben, um festzustellen, ob doppelte Anfragen erzeugt oder ordnungsgemäß verhindert werden.
- Verwenden Sie in einem Debug-Build StrictMode, um nach weiteren unbeabsichtigten Netzwerkzugriffen im Hauptthread zu suchen. Stellen Sie anschließend die vorgesehene Debug-Richtlinie wieder her.
- Prüfen Sie den neuen Stacktrace, falls eine andere Ausnahme auftritt. Diagnostizieren Sie die neue Fehlersignatur separat, statt sie als weiteren Fall von
NetworkOnMainThreadExceptionzu behandeln.
Die Korrektur der Thread-Zuordnung ist bestätigt, wenn der ursprüngliche Ablauf keine blockierende Netzwerkarbeit mehr im Hauptthread ausführt und alle zugehörigen UI-Änderungen weiterhin dort erfolgen. Eine erfolgreiche Antwort des Endpunkts allein beweist nicht, dass die Thread-Zuordnung korrekt ist. Das Beheben dieser Ausnahme garantiert außerdem nicht, dass die Netzwerkanfrage selbst erfolgreich sein wird.
Fehler und Umgehungslösungen, die nicht zu dieser Korrektur gehören
| Meldung oder Vorgehensweise | Warum dies ein anderer Fall ist |
|---|---|
UnknownHostException |
Dies ist ein anderer Fehler bei der Hostauflösung oder Erreichbarkeit und kein Beleg dafür, dass Netzwerkarbeit im Hauptthread ausgeführt wurde. |
SSLHandshakeException |
Dies ist ein separater TLS-Fehler, der eigenständig diagnostiziert werden muss. |
| Fehlende INTERNET-Berechtigung | Dies ist ein Berechtigungs- oder Konfigurationsproblem und nicht die maßgebliche Ursache von NetworkOnMainThreadException. |
| Fehler aufgrund der Klartext-Richtlinie | Dies ist eine Einschränkung der Netzwerksicherheitsrichtlinie für unverschlüsselten Datenverkehr und kein Verstoß durch eine Ausführung im Hauptthread. |
CalledFromWrongThreadException |
Diese Ausnahme betrifft den Zugriff auf Views aus dem falschen Thread, nachdem die Arbeit den Kontext gewechselt hat. Sie ist nicht mit dieser Ausnahme identisch. |
permitAll() oder permitNetwork() |
Diese Methoden lockern die Diagnosebeschränkung, statt die Thread-Zuordnung des blockierenden Vorgangs zu korrigieren. |
| Veraltete AsyncTask-Anleitungen | AsyncTask ist veraltet und nicht der bevorzugte moderne Reparaturweg. |
Beginnen Sie nicht mit der Fehlersuche bei TLS, DNS-Erreichbarkeit, Berechtigungen oder Klartextdatenverkehr, wenn der tatsächliche Fehler android.os.NetworkOnMainThreadException lautet. Korrigieren Sie zuerst die Thread-Zuordnung. Falls das Verlagern einer HTTP-Anfrage aus dem Hauptthread anschließend den separaten Fehler Cleartext HTTP traffic not permitted sichtbar macht, diagnostizieren Sie diesen neuen Fehler unabhängig davon.

