Frage Wie behebe ich android.os.NetworkOnMainThreadException?


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?


1969
2018-06-14 12:02


Ursprung


Antworten:


Diese Ausnahme wird ausgelöst, wenn eine Anwendung versucht, eine Netzwerkoperation für ihren Hauptthread auszuführen. Führen Sie Ihren Code ein AsyncTask:

class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> {

    private Exception exception;

    protected RSSFeed doInBackground(String... urls) {
        try {
            URL url = new URL(urls[0]);
            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();
        } catch (Exception e) {
            this.exception = e;

            return null;
        } finally {
            is.close();
        }
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

So führen Sie die Aufgabe aus:

Im MainActivity.java Datei können Sie diese Zeile in Ihrem hinzufügen oncreate() Methode

new RetrieveFeedTask().execute(urlToRssFeed);

Vergessen Sie nicht, dies hinzuzufügen AndroidManifest.xml Datei:

<uses-permission android:name="android.permission.INTERNET"/>

2232
2018-06-14 12:15



Sie sollten Netzwerkoperationen fast immer für einen Thread oder als asynchrone Task ausführen.

Aber es ist Sie können diese Einschränkung entfernen und das Standardverhalten überschreiben, wenn Sie bereit sind, die Konsequenzen zu akzeptieren.

Hinzufügen:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

StrictMode.setThreadPolicy(policy); 

In deiner Klasse,

und

Fügen Sie diese Berechtigung in der Datei android manifest.xml hinzu:

<uses-permission android:name="android.permission.INTERNET"/>

Folgen:

Ihre App wird (in Bereichen mit unterbrochener Internetverbindung) nicht mehr reagiert und blockiert, der Benutzer nimmt Langsamkeit wahr und muss erzwungen werden, und Sie riskieren, dass der Aktivitätsmanager Ihre App beendet und dem Benutzer mitteilt, dass die App beendet wurde.

Android bietet einige gute Tipps zu guten Programmierpraktiken für die Reaktionsfähigkeit: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


556
2018-02-15 06:59



Ich habe dieses Problem mit einem neuen Problem gelöst Thread.

Thread thread = new Thread(new Runnable() {

    @Override
    public void run() {
        try  {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

thread.start(); 

346
2018-01-21 16:31



Sie können kein Netzwerk ausführen I / O auf dem UI-Thread auf Bienenwabe. Technisch gesehen ist Dies ist jedoch eine sehr schlechte Idee, da Ihre App nicht mehr reagiert und das Betriebssystem Ihre App zum schlechten Benehmen bringt. Sie müssen einen Hintergrundprozess ausführen oder AsyncTask verwenden, um Ihre Netzwerktransaktion in einem Hintergrundthread auszuführen.

Es gibt einen Artikel über Schmerzloses Einfädeln auf der Android-Entwickler-Website, die eine gute Einführung in diese ist, und es wird Ihnen eine viel bessere Tiefe einer Antwort bieten, als realistisch hier zur Verfügung gestellt werden kann.


124
2018-06-14 12:07



Die angenommene Antwort hat einige signifikante Nachteile. Es ist nicht ratsam, AsyncTask für das Netzwerk zu verwenden, außer Sie Ja wirklich weiß was du tust. Einige der Down-Seiten umfassen:

  • AsyncTask-Objekte, die als nicht statische innere Klassen erstellt wurden, haben einen impliziten Verweis auf das umschließende Activity-Objekt, seinen Kontext und die gesamte View-Hierarchie, die von dieser Aktivität erstellt wurde. Diese Referenz verhindert, dass die Aktivität als Garbage Collection erfasst wird, bis die Hintergrundarbeit von AsyncTask abgeschlossen ist. Wenn die Verbindung des Benutzers langsam ist und / oder der Download groß ist, können diese Kurzzeitspeicherlecks zum Problem werden - beispielsweise wenn sich die Ausrichtung mehrmals ändert (und Sie die ausgeführten Aufgaben nicht abbrechen) oder der Benutzer navigiert weg von der Aktivität.
  • Abhängig von der Plattform, auf der AsyncTask ausgeführt wird, hat AsyncTask unterschiedliche Ausführungsmerkmale: vor der API-Stufe 4 werden AsyncTasks seriell auf einem einzelnen Hintergrund-Thread ausgeführt. Von API-Ebene 4 bis API-Ebene 10 werden AsyncTasks in einem Pool von bis zu 128 Threads ausgeführt. ab API-Level 11 wird AsyncTask seriell auf einem einzelnen Hintergrund-Thread ausgeführt (es sei denn, Sie verwenden das Überladene executeOnExecutorMethode und liefern einen alternativen Executor). Code, der gut funktioniert, wenn er seriell auf ICS ausgeführt wird, kann bei der gleichzeitigen Ausführung auf Gingerbread unterbrochen werden, wenn Sie unbeabsichtigte Abhängigkeitsreihenfolge haben.

Wenn Sie Kurzzeitspeicherlecks vermeiden wollen, über gut definierte Ausführungsmerkmale auf allen Plattformen verfügen und eine Basis für eine wirklich robuste Netzwerkbehandlung haben, sollten Sie Folgendes berücksichtigen:

  1. Verwenden Sie eine Bibliothek, die eine gute Arbeit für Sie erledigt - es gibt einen schönen Vergleich von Netzwerk-Bibliotheken in diese Frage, oder
  2. Verwendung einer Service oder IntentService stattdessen vielleicht mit einem PendingIntent um das Ergebnis über die Aktivität zurückzugeben onActivityResult Methode.

IntentService-Ansatz

Downside:

  • Mehr Code und Komplexität als AsyncTaskobwohl nicht so viel, wie du vielleicht denkst
  • Werden Anfragen in Warteschlange stellen und sie auf einem ausführen Single Hintergrundfaden. Sie können dies leicht durch Ersetzen ersetzen IntentService mit einem Äquivalent Service Implementierung, vielleicht wie dieses.
  • Ähm, im Moment kann ich eigentlich gar nicht an andere denken

Up-Seiten:

  • Vermeidet das Problem des Kurzzeitspeicherverlustes
  • Wenn Ihre Aktivität während des laufenden Betriebs des Netzwerks neu gestartet wird, kann sie das Ergebnis des Downloads weiterhin über das Programm erhalten onActivityResult Methode
  • Bessere Plattform als AsyncTask zum Erstellen und Wiederverwenden von robustem Netzwerkcode. Beispiel: Wenn Sie einen wichtigen Upload durchführen müssen, können Sie dies tun AsyncTask in einem (n Activity, aber wenn der Benutzerkontext-aus der App ausschaltet, um einen Anruf anzunehmen, das System kann 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 gleichzeitige Version von verwenden IntentService (wie die, die ich oben verlinkt habe), können Sie den Grad der Parallelität über die Executor.

Implementierungszusammenfassung

Sie können ein implementieren IntentService Downloads auf einem einzelnen Hintergrund-Thread ganz einfach durchzuführen.

Schritt 1: Erstellen Sie ein IntentService um den Download durchzuführen. Sie können ihm sagen, was Sie herunterladen sollen Intent extra, und übergeben Sie es a PendingIntent zu verwenden, um das Ergebnis an die 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 re-use, 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 von der Aktivität auf und übergeben Sie ein PendingResult-Objekt, das der Service zum Zurückgeben des Ergebnisses verwendet:

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 mit einem vollständig funktionierenden Android-Studio / Gradle-Projekt ist verfügbar Hier.


118
2018-01-22 13:17



  1. Verwenden Sie strictMode nicht (nur im Debug-Modus)
  2. Ändern Sie nicht die SDK-Version
  3. Verwenden Sie keinen separaten Thread

Verwenden Sie Service oder AsyncTask

Siehe auch Stapelüberlauffrage:

android.os.NetworkOnMainThreadException sendet eine E-Mail von Android


59
2017-08-18 09:18



Führen Sie die Netzwerkaktionen in einem anderen Thread aus

Beispielsweise:

new Thread(new Runnable(){
    @Override
    public void run() {
        // Do network action in this function
    }
}).start();

Und füge das zu AndroidManifest.xml hinzu

<uses-permission android:name="android.permission.INTERNET"/>

51
2017-12-24 14:33



Sie deaktivieren den strikten Modus mit folgendem Code:

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = 
        new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

Dies wird nicht empfohlen: Benutze die AsyncTaskSchnittstelle.

Vollständiger Code für beide Methoden


46
2017-10-24 07:10



Netzwerkbasierte Vorgänge können nicht im Hauptthread ausgeführt werden. Sie müssen alle netzwerkbasierten Tasks für einen untergeordneten Thread ausführen oder AsyncTask implementieren.

So führen Sie eine Aufgabe in einem untergeordneten Thread aus:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

39
2018-01-14 06:14



Setze deinen Code hinein:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

Oder:

class DemoTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... arg0) {
        //Your implementation
    }

    protected void onPostExecute(Void result) {
        // TODO: do something with the feed
    }
}

37
2017-07-28 10:43