Beim Ausführen meines Android-Projekts für RssReader ist ein Fehler aufgetreten.
Code:
URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();
Und es zeigt den folgenden Fehler:
android.os.NetworkOnMainThreadException
Wie kann ich dieses Problem beheben?
Sie sollten Netzwerkvorgänge fast immer in einem Thread oder als asynchrone Aufgabe ausführen.
Aber es ist Es ist möglich, diese Einschränkung aufzuheben, und Sie setzen das Standardverhalten außer Kraft, wenn Sie bereit sind, die Konsequenzen zu akzeptieren.
Addieren:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
In deiner Klasse,
und
Addieren diese Berechtigung im Android manifest.xml Datei:
<uses-permission android:name="android.permission.INTERNET"/>
Konsequenzen:
Ihre App wird (in Bereichen mit lückenhafter Internetverbindung) nicht mehr reagieren und abstürzen, der Benutzer nimmt die Langsamkeit wahr und muss einen Force-Kill durchführen, und Sie riskieren, dass der Aktivitätsmanager Ihre App beendet und dem Benutzer mitteilt, dass die App beendet wurde.
Android hat einige gute Tipps zu guten Programmierpraktiken für ein reaktionsschnelles Design:
NetworkOnMainThreadException | Android-Entwickler
Die akzeptierte Antwort hat einige erhebliche Nachteile. Es ist nicht ratsam, AsyncTask für das Netzwerk zu verwenden, es sei denn, Sie Ja wirklich weiß was du tust. Einige der Nachteile sind:
- AsyncTasks, die als nicht statische innere Klassen erstellt wurden, haben einen impliziten Verweis auf das einschließende Activity-Objekt, seinen Kontext und die gesamte View-Hierarchie, die von dieser Aktivität erstellt wurde. Dieser Verweis verhindert, dass die Aktivität von der Garbage Collection erfasst wird, bis die Hintergrundarbeit von AsyncTask abgeschlossen ist. Wenn die Verbindung des Benutzers langsam und/oder der Download groß ist, können diese Kurzzeitgedächtnislecks zu einem Problem werden – zum Beispiel, wenn sich die Orientierung mehrmals ändert (und Sie die auszuführenden Aufgaben nicht abbrechen) oder der Benutzer navigiert von der Aktivität weg.
- AsyncTask hat je nach Plattform, auf der es ausgeführt wird, unterschiedliche Ausführungsmerkmale: vor API-Ebene 4 werden AsyncTasks seriell auf einem einzelnen Hintergrund-Thread ausgeführt; von API-Level 4 bis API-Level 10 werden AsyncTasks auf einem Pool von bis zu 128 Threads ausgeführt; Ab API-Level 11 wird AsyncTask seriell auf einem einzelnen Hintergrundthread ausgeführt (es sei denn, Sie verwenden die überladene
executeOnExecutor
Methode und Bereitstellung eines alternativen Ausführenden). Code, der gut funktioniert, wenn er seriell auf ICS ausgeführt wird, kann bei gleichzeitiger Ausführung auf Gingerbread brechen, z. B. wenn Sie unbeabsichtigte Abhängigkeiten in der Ausführungsreihenfolge haben.
Wenn Sie kurzfristige Speicherlecks vermeiden möchten, über gut definierte Ausführungsmerkmale auf allen Plattformen verfügen und eine Basis haben möchten, um eine wirklich robuste Netzwerkverarbeitung aufzubauen, sollten Sie Folgendes in Betracht ziehen:
- Verwenden Sie eine Bibliothek, die dies gut für Sie erledigt – in dieser Frage finden Sie einen schönen Vergleich von Netzwerkbibliotheken, oder
- Verwendung einer
Service
oder IntentService
stattdessen vielleicht mit a PendingIntent
um das Ergebnis über die Aktivität zurückzugeben onActivityResult
Methode.
IntentService-Ansatz
Nachteile:
- Mehr Code und Komplexität als
AsyncTask
wenn auch nicht so viel, wie Sie vielleicht denken
- Wird Anfragen in die Warteschlange stellen und sie auf a ausführen Einzel Hintergrundthread. Sie können dies leicht durch Ersetzen steuern
IntentService
mit einem Äquivalent Service
Umsetzung, vielleicht wie Dieses hier.
- Ähm, mir fallen gerade keine anderen ein
Vorteile:
- Vermeidet das Problem des Kurzzeitgedächtnisses
- Wenn Ihre Aktivität neu gestartet wird, während der Netzwerkbetrieb läuft, kann sie das Ergebnis des Downloads dennoch über seine empfangen
onActivityResult
Methode
- Eine bessere Plattform als AsyncTask, um robusten Netzwerkcode zu erstellen und wiederzuverwenden. Beispiel: Wenn Sie einen wichtigen Upload durchführen müssen, können Sie dies von tun
AsyncTask
in einem (n Activity
aber wenn der Benutzer den Kontext der App verlässt, um einen Anruf entgegenzunehmen, wird das system dürfen Beenden Sie die App, bevor der Upload abgeschlossen ist. es ist weniger wahrscheinlich um eine Anwendung mit einem aktiven zu beenden Service
.
- Wenn Sie Ihre eigene Concurrent-Version von
IntentService
(wie die, die ich oben verlinkt habe) können Sie den Grad der Parallelität über die steuern Executor
.
Zusammenfassung der Umsetzung
Sie können eine implementieren IntentService
um ganz einfach Downloads auf einem einzelnen Hintergrund-Thread durchzuführen.
Schritt 1: Erstellen Sie eine IntentService
um den Download durchzuführen. Sie können ihm mitteilen, was heruntergeladen werden soll Intent
Extras, und übergeben Sie es a PendingIntent
verwenden, um das Ergebnis an die zurückzugeben Activity
:
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
public class DownloadIntentService extends IntentService {
private static final String TAG = DownloadIntentService.class.getSimpleName();
public static final String PENDING_RESULT_EXTRA = "pending_result";
public static final String URL_EXTRA = "url";
public static final String RSS_RESULT_EXTRA = "url";
public static final int RESULT_CODE = 0;
public static final int INVALID_URL_CODE = 1;
public static final int ERROR_CODE = 2;
private IllustrativeRSSParser parser;
public DownloadIntentService() {
super(TAG);
// make one and reuse, in the case where more than one intent is queued
parser = new IllustrativeRSSParser();
}
@Override
protected void onHandleIntent(Intent intent) {
PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA);
InputStream in = null;
try {
try {
URL url = new URL(intent.getStringExtra(URL_EXTRA));
IllustrativeRSS rss = parser.parse(in = url.openStream());
Intent result = new Intent();
result.putExtra(RSS_RESULT_EXTRA, rss);
reply.send(this, RESULT_CODE, result);
} catch (MalformedURLException exc) {
reply.send(INVALID_URL_CODE);
} catch (Exception exc) {
// could do better by treating the different sax/xml exceptions individually
reply.send(ERROR_CODE);
}
} catch (PendingIntent.CanceledException exc) {
Log.i(TAG, "reply cancelled", exc);
}
}
}
Schritt 2: Registrieren Sie den Dienst im Manifest:
<service
android:name=".DownloadIntentService"
android:exported="false"/>
Schritt 3: Rufen Sie den Dienst aus der Aktivität auf und übergeben Sie ein PendingResult-Objekt, das der Dienst verwendet, um das Ergebnis zurückzugeben:
PendingIntent pendingResult = createPendingResult(
RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class);
intent.putExtra(DownloadIntentService.URL_EXTRA, URL);
intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult);
startService(intent);
Schritt 4: Behandeln Sie das Ergebnis in onActivityResult:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) {
switch (resultCode) {
case DownloadIntentService.INVALID_URL_CODE:
handleInvalidURL();
break;
case DownloadIntentService.ERROR_CODE:
handleError(data);
break;
case DownloadIntentService.RESULT_CODE:
handleRSS(data);
break;
}
handleRSS(data);
}
super.onActivityResult(requestCode, resultCode, data);
}
Ein GitHub-Projekt, das ein vollständig funktionierendes Android Studio enthält/Gradl Projekt liegt vor Hier.
Sie können kein Netzwerk ausführen E/A auf dem UI-Thread auf Bienenwabe. Technisch gesehen ist es ist auf früheren Versionen von Android möglich, aber es ist eine wirklich schlechte Idee, da es dazu führt, dass Ihre App nicht mehr reagiert, und dazu führen kann, dass das Betriebssystem Ihre App wegen schlechten Benehmens beendet. Sie müssen einen Hintergrundprozess ausführen oder AsyncTask verwenden, um Ihre Netzwerktransaktion in einem Hintergrundthread auszuführen.
Es gibt einen Artikel darüber Schmerzloses Einfädeln auf der Android-Entwicklerseite, die eine gute Einführung in dieses Thema darstellt und Ihnen eine viel bessere Antworttiefe bietet, als dies hier realistisch möglich ist.
Es gibt zwei Lösungen für dieses Problem.
-
Verwenden Sie im Haupt-UI-Thread keinen Netzwerkaufruf. Verwenden Sie dafür eine asynchrone Aufgabe.
-
Schreiben Sie danach den folgenden Code in Ihre MainActivity-Datei setContentView (R.layout.activity_main);:
if (android.os.Build.VERSION.SDK_INT > 9) {StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy (Richtlinie); }
Und die folgende import-Anweisung in Ihre Java-Datei.
import android.os.StrictMode;
Lesen Sie diesen Blogbeitrag auf der NetworkOnMainThreadException für weitere Informationen. Es erklärt, warum dies bei Android 3.0 und höher auftritt.
– Adrian Mönch
7. August 2012 um 12:38 Uhr
Um auf dem richtigen Weg zu sein, lesen Sie zuerst etwas über die Netzwerkanfragen in Android, dann würde ich empfehlen, “Volley” zu studieren.
– Anuj Sharma
23. Januar 2014 um 6:39 Uhr
Es gibt viele alternative Bibliotheken, die dieses Problem lösen. Viele sind aufgelistet unten auf dieser Seite. Wenn du mehr hast, nehmen wir sie 🙂
– Snikola
11. Februar 2014 um 22:55 Uhr
Sie müssen Internetaktivitäten in einem Thread ausführen, der vom Hauptthread (UI) getrennt ist
– Naveed Ahmad
30. Oktober 2014 um 20:44 Uhr
“Aufgrund eines Fehlers in früheren Versionen von Android hat das System das Schreiben in einen TCP-Socket im Haupt-Thread nicht als Verstoß gegen den strikten Modus gekennzeichnet. Android 7.0 behebt diesen Fehler. Apps, die dieses Verhalten aufweisen, werfen jetzt ein android.os. NetworkOnMainThreadException.” – Also einige von uns haben das bis vor kurzem nicht getroffen! developer.android.com/about/versions/nougat/…
– Jay
19. November 2017 um 22:51 Uhr