Domanda Come posso risolvere android.os.NetworkOnMainThreadException?


Ho avuto un errore durante l'esecuzione del mio progetto Android per RssReader.

Codice:

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();

E mostra l'errore seguente:

android.os.NetworkOnMainThreadException

Come posso risolvere questo problema?


1969
2018-06-14 12:02


origine


risposte:


Questa eccezione viene generata quando un'applicazione tenta di eseguire un'operazione di rete sul thread principale. Esegui il tuo codice 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
    }
}

Come eseguire l'attività:

In MainActivity.java file puoi aggiungere questa linea nel tuo oncreate() metodo

new RetrieveFeedTask().execute(urlToRssFeed);

Non dimenticare di aggiungere questo a AndroidManifest.xml file:

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

2232
2018-06-14 12:15



Dovresti quasi sempre eseguire operazioni di rete su un thread o un'attività asincrona.

Ma ciò è è possibile rimuovere questa restrizione e si sostituisce il comportamento predefinito, se si è disposti ad accettare le conseguenze.

Inserisci:

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

StrictMode.setThreadPolicy(policy); 

Nella tua classe,

e

AGGIUNGI questa autorizzazione nel file manifest.xml di Android:

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

conseguenze:

La tua app (in aree di scarsa connessione internet) non risponde e si blocca, l'utente percepisce la lentezza e deve fare un kill forzato, e rischi il gestore delle attività che uccide la tua app e che dice all'utente che l'app si è fermata.

Android ha alcuni buoni consigli su buone pratiche di programmazione per progettare per la reattività: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


556
2018-02-15 06:59



Ho risolto questo problema utilizzando un nuovo 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



Non è possibile eseguire la rete I / O sul thread dell'interfaccia utente Favo. Tecnicamente è possibile su versioni precedenti di Android, ma è una pessima idea in quanto causerà l'interruzione della risposta della tua app e può comportare il blocco dell'OS della tua app per il cattivo funzionamento. È necessario eseguire un processo in background o utilizzare AsyncTask per eseguire la transazione di rete su un thread in background.

C'è un articolo su Threading indolore sul sito degli sviluppatori Android, che è una buona introduzione a questo, e ti fornirà una profondità di risposta molto migliore di quella che può essere realisticamente fornita qui.


124
2018-06-14 12:07



La risposta accettata ha alcuni lati negativi significativi. Non è consigliabile utilizzare AsyncTask per il networking a meno che tu veramente sai cosa stai facendo Alcuni dei lati negativi includono:

  • AsyncTask creato come classi interne non statiche ha un riferimento implicito all'oggetto Activity che lo racchiude, al suo contesto e all'intera gerarchia View creata da quell'attività. Questo riferimento impedisce che l'attività venga raccolta tramite garbage collector fino al completamento del lavoro in background di AsyncTask. Se la connessione dell'utente è lenta e / o il download è grande, queste perdite di memoria a breve termine possono diventare un problema, ad esempio se l'orientamento cambia più volte (e non si annullano le attività in esecuzione) o l'utente naviga lontano dall'attività.
  • AsyncTask ha caratteristiche di esecuzione diverse a seconda della piattaforma su cui viene eseguito: prima dell'API di livello 4 AsyncTasks esegue in modo seriale su un singolo thread in background; dal livello API 4 al livello API 10, AsyncTasks esegue su un pool di fino a 128 thread; dal livello API 11 in poi AsyncTask viene eseguito in serie su un singolo thread in background (a meno che non si usi il sovraccarico executeOnExecutormetodo e fornire un esecutore alternativo). Il codice che funziona bene quando viene eseguito in serie su ICS può interrompersi se eseguito simultaneamente su Gingerbread, ad esempio se si hanno delle dipendenze di ordine di esecuzione involontarie.

Se si desidera evitare perdite di memoria a breve termine, avere caratteristiche di esecuzione ben definite su tutte le piattaforme e avere una base per costruire una gestione di rete davvero solida, si potrebbe prendere in considerazione:

  1. Usando una libreria che fa un buon lavoro per te - c'è un bel confronto delle librerie di rete in questa domanda, o
  2. Usare un Service o IntentService invece, forse con a PendingIntent per restituire il risultato tramite le attività onActivityResult metodo.

Approccio IntentService

Aspetti negativi:

  • Più codice e complessità di AsyncTask, anche se non tanto quanto potresti pensare
  • Far accodare richieste ed eseguirle su a singolo thread di sfondo. Puoi facilmente controllarlo sostituendo IntentService con un equivalente Service implementazione, forse come questo.
  • Um, non riesco a pensare a nessun altro in questo momento

Up-lati:

  • Evita il problema della perdita di memoria a breve termine
  • Se la tua attività si riavvia mentre le operazioni di rete sono in volo, può comunque ricevere il risultato del download tramite la sua onActivityResult metodo
  • Piattaforma migliore di AsyncTask per creare e riutilizzare un robusto codice di rete. Esempio: se devi eseguire un caricamento importante, puoi farlo da AsyncTask in un Activity, ma se il contesto utente-si disconnette dall'app per fare una telefonata, il sistema potrebbe uccidere l'app prima del completamento dell'upload. È meno probabile per uccidere un'applicazione con un attivo Service.
  • Se si utilizza la propria versione simultanea di IntentService (come quello che ho linkato sopra) puoi controllare il livello di concorrenza tramite Executor.

Riepilogo dell'implementazione

Puoi implementare un IntentService eseguire i download su un singolo thread in background abbastanza facilmente.

Passaggio 1: creare un IntentService per eseguire il download. Puoi dirgli cosa scaricare tramite Intent extra, e passarlo a PendingIntent da usare per restituire il risultato al 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);
        }
    }
}

Passaggio 2: registrare il servizio nel manifest:

<service
        android:name=".DownloadIntentService"
        android:exported="false"/>

Passaggio 3: Richiamare il servizio dall'attività, passando un oggetto PendingResult che verrà utilizzato dal servizio per restituire il risultato:

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);

Passaggio 4: gestire il risultato 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);
}

È disponibile un progetto github contenente un progetto Android-Studio / gradle funzionante Qui.


118
2018-01-22 13:17



  1. Non usare strictMode (solo in modalità debug)
  2. Non modificare la versione dell'SDK
  3. Non usare una filettatura separata

Utilizzare il servizio o AsyncTask

Vedi anche Domanda di overflow dello stack:

android.os.NetworkOnMainThreadException invia un'email da Android


59
2017-08-18 09:18



Effettua le azioni di rete su un altro thread

Per esempio:

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

E aggiungilo a AndroidManifest.xml

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

51
2017-12-24 14:33



Disabilita la modalità rigorosa usando il seguente codice:

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

Questo non è raccomandato: Usa il AsyncTaskinterfaccia.

Codice completo per entrambi i metodi


46
2017-10-24 07:10