Come risolvere 'android.os.NetworkOnMainThreadException'?


2394

Ho riscontrato 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?


131
Leggi questo post sul blog su NetworkOnMainThreadException per ulteriori informazioni. Spiega perché ciò si verifica su Android 3.0 e versioni successive.
Adrian Monk,

6
Per essere sulla pista di rito leggi prima le Richieste di rete su Android, quindi consiglierei di studiare "Volley".
Anuj Sharma,

3
Esistono molte librerie alternative che risolvono questo problema. Molti sono elencati in fondo a questa pagina . Se ne hai di più, li prendiamo :)
Snicolas,

Devi eseguire attività su Internet su un thread separato dal thread principale (UI)
Naveed Ahmad,

"A causa di un bug nelle versioni precedenti di Android, il sistema non segnalava la scrittura su un socket TCP sul thread principale come violazione della modalità rigorosa. Android 7.0 corregge questo bug. Le app che presentano questo comportamento ora generano un android.os. NetworkOnMainThreadException." - Quindi alcuni di noi non ci sono riusciti fino a poco tempo fa! developer.android.com/about/versions/nougat/…
Jay

Risposte:


2547

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

Nel MainActivity.javafile puoi aggiungere questa riga nel tuo oncreate()metodo

new RetrieveFeedTask().execute(urlToRssFeed);

Non dimenticare di aggiungere questo al AndroidManifest.xmlfile:

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

37
Penso che valga la pena notare che lo snippet di codice sopra riportato dovrebbe essere una sottoclasse (classe interna), preferibilmente privata. In questo modo, quando AsyncTask termina, puoi ancora manipolare le viscere della tua classe.
dyslexicanaboko,

4
In realtà ho fatto la stessa cosa che hai menzionato sopra ma sto
Dhruv Tyagi,

68
Questa è esattamente la risposta sbagliata. Mi capita sempre di trovarlo nel codice delle persone, ed è fastidioso doverlo sistemare continuamente. AsyncTask non deve essere utilizzato per l'attività di rete, perché è legato all'attività, ma non al ciclo di vita dell'attività. La rotazione del dispositivo con questa attività in esecuzione provoca un'eccezione e arresta in modo anomalo l'app. Utilizzare invece IntentService che elimina i dati nel database sqlite.
Brill Pappin,

5
Attenzione, AsyncTask viene spesso utilizzato per operazioni di rete per attività quando non dovrebbe esserlo. il suo ciclo di vita non è sincronizzato con l'attività. Per il recupero dei dati, è necessario utilizzare un IntentService e il database dietro la vista.
Brill Pappin,

1
@BrillPappin, FWIW, questo androide Guida dello sviluppatore usi AsyncTask, e si prende cura di una modifica alla configurazione.
HeyJude,

677

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

Ma è possibile rimuovere questa restrizione e sovrascrivere il comportamento predefinito, se si è disposti ad accettarne 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 con scarsa connessione a Internet) non risponderà e si bloccherà, l'utente percepirà la lentezza e dovrà uccidere forzatamente, e rischi che il manager delle attività uccida la tua app e dica all'utente che l'app si è fermata.

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


456
Questa è una pessima idea. la soluzione è evitare l'IO di rete sul thread principale (come mostra la risposta accettata).
MByD

74
Con questo nascondi solo il tuo vero problema.
Alex,

28
@TwistedUmbrella AsyncTask non aggiunge una pagina di codice, ma aggiunge 6 righe (dichiarazione di classe, annotazione override, doInBackgrounddichiarazione, 2 parentesi quadre e una chiamata a execute()). D'altra parte, anche un singolo recupero da un sito, come accennato, porta un ritardo significativo nella reattività dell'interfaccia utente. Non essere pigro.
Zoltán,

19
Penso che questa sia la soluzione perfetta se stai cercando di eseguire solo un campione di codice per vedere se qualcosa funziona prima di implementare l'AsyncTask corretto. Ecco perché ho votato a favore di questa risposta, sebbene, come tutti gli altri hanno già detto, ciò non dovrebbe essere fatto in un'app di produzione, ma solo come soluzione rapida per un test di sviluppo.
hooked82

94
Upvoted. Questa risposta è corretta, e per i molti programmatori che non sono né ingenui né stupidi, ma che richiedono semplicemente una chiamata SINCRONA (cioè: deve bloccare l'app ), questo è esattamente ciò che è necessario. Sono più che felice che Android generi l'eccezione per impostazione predefinita (IMHO è una cosa molto "utile" da fare!) - ma sono altrettanto felice di dire "grazie, ma - questo è in realtà quello che volevo" e ignorare esso.
Adam,

428

Ho risolto questo problema usando 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(); 

7
Invece di creare un nuovo thread ogni volta che si desidera eseguire un'operazione di rete, è possibile utilizzare anche un singolo servizio di esecuzione thread.
Alex Lockwood,

66
Semplice ma pericoloso Il Runnable anonimo ha un riferimento implicito alla classe che lo racchiude (ad esempio la tua attività o frammento), impedendo che venga raccolta in modo inutile fino al completamento del thread. Dovresti almeno impostare la priorità su Process.BACKGROUND, altrimenti questo thread verrà eseguito con la stessa priorità del thread main / ui, contendendo i metodi del ciclo di vita e la frequenza dei fotogrammi dell'interfaccia utente (fai attenzione agli avvisi nel log del coreografo).
Stevie,

1
@Stevie come impostare la priorità? né runnble né ilecutorService hanno un tale metodo setter
JK

1
@JK Fornisci al tuo ExecutorService un ThreadFactory personalizzato e chiama Thread.setPriority sul thread prima di restituirlo.
Stevie,

1
"L'uso dei thread direttamente in Android è scoraggiato. Causa più problemi di quanti ne risolva" Ti interessa approfondire? In realtà AsyncTask è stato deprecato esattamente per questo ... techyourchance.com/asynctask-deprecated
Fran

170

La risposta accettata presenta alcuni lati negativi significativi. Non è consigliabile utilizzare AsyncTask per il networking, a meno che non si sappia davvero cosa si sta facendo. Alcuni degli aspetti negativi includono:

  • Gli AsyncTask creati come classi interne non statiche hanno un riferimento implicito all'oggetto Activity allegato, al suo contesto e all'intera gerarchia View creata da quell'attività. Questo riferimento impedisce che l'attività venga raccolta in modo inutile 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 si allontana dall'attività.
  • AsyncTask presenta caratteristiche di esecuzione diverse a seconda della piattaforma su cui viene eseguita: prima del livello API 4 AsyncTasks viene eseguito in serie su un singolo thread in background; dal livello API 4 al livello API 10, AsyncTasks viene eseguito su un pool 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 executeOnExecutormetodo sovraccarico e si fornisca un esecutore alternativo). Il codice che funziona correttamente durante l'esecuzione in serie su ICS potrebbe interrompersi se eseguito contemporaneamente su Gingerbread, ad esempio se si hanno dipendenze involontarie sull'ordine di esecuzione.

Se vuoi 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 della rete davvero solida, potresti prendere in considerazione:

  1. Usando una libreria che fa un buon lavoro per te - c'è un bel confronto tra le librerie di rete in questa domanda , o
  2. Utilizzando a Serviceo IntentServiceinvece, forse con a PendingIntentper restituire il risultato tramite il onActivityResultmetodo dell'Attività.

Approccio IntentService

Aspetti negativi:

  • Più codice e complessità rispetto a AsyncTask, anche se non tanto quanto si potrebbe pensare
  • Accoderà le richieste ed eseguirle su un singolo thread in background. Puoi controllarlo facilmente sostituendolo IntentServicecon Serviceun'implementazione equivalente , forse come questa .
  • Uhm, non riesco a pensare ad altri in questo momento in realtà

Up-sides:

  • Evita il problema di perdita di memoria a breve termine
  • Se la tua attività viene riavviata mentre le operazioni di rete sono in volo, può comunque ricevere il risultato del download tramite il suo onActivityResultmetodo
  • Piattaforma migliore di AsyncTask per creare e riutilizzare un robusto codice di rete. Esempio: se è necessario eseguire un caricamento importante, è possibile farlo da AsyncTaskin Activity, ma se l'utente contestuale esce dall'app per rispondere a una telefonata, il sistema potrebbe terminare l'app prima del completamento del caricamento. È meno probabile che uccida un'applicazione con un attivo Service.
  • Se usi la tua versione simultanea di IntentService(come quella che ho linkato sopra) puoi controllare il livello di concorrenza tramite il Executor.

Riepilogo dell'implementazione

È possibile implementare un IntentServiceper eseguire download su un singolo thread in background abbastanza facilmente.

Passaggio 1: creare un IntentServiceper eseguire il download. Puoi dirgli cosa scaricare tramite Intentextra e passarlo a PendingIntentda usare per restituire il risultato a 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 il servizio utilizzerà 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);
}

Un progetto Github contenente un progetto Android-Studio / Gradle funzionante completo è disponibile qui .


IntentService è il modo corretto per farlo, non lo sradicamento perché AsyncTask è esattamente il modo di non farlo.
Brill Pappin,

3
@BrillPappin Sono quasi del tutto d'accordo e ho riformulato per enfatizzare gli svantaggi di AsyncTask. (Penso ancora che ci siano un numero molto piccolo di casi in cui - se sai davvero cosa stai facendo - potrebbe essere OK usare AsyncTask, ma la risposta accettata non evidenzia alcun inconveniente ed è troppo popolare per il bene di Android).
Stevie,

1
Ne hai davvero bisogno IllustrativeRSS? Cosa succede se non lavori con roba RSS?
Cullub,

A mio avviso, Google dovrebbe cambiare la loro cattiva implementazione della garbage collection piuttosto che mettere il berdon dalla parte dei programmatori. Questa è la responsabilità del sistema operativo. Se un programmatore utilizza IntentService o Service per svolgere il lavoro a causa del problema fondamentale nell'implementazione di Android, dopo alcuni anni Google dirà che anche IntentService è una cattiva pratica e suggerisce qualcos'altro. E questa storia continua ... Quindi gli sviluppatori di Google dovrebbero risolvere la cattiva gestione della memoria Android non i programmatori.
saeed khalafinejad,

Sto solo per dirlo: questa risposta sembra più un modo di condividere il progetto che un'alternativa migliore a AsyncTask. L'errore aveva lo scopo di impedire agli sviluppatori di rallentare l'interfaccia utente, non necessariamente indurli a rischiare la sicurezza con un sacco di intenzioni / servizi.
Carrello abbandonato

144

Non è possibile eseguire l' I / O di rete sul thread dell'interfaccia utente su Honeycomb . Tecnicamente, è possibile con le versioni precedenti di Android, ma è una pessima idea in quanto farà sì che l'app non risponda e che il sistema operativo possa uccidere l'app per comportamenti scorretti. È necessario eseguire un processo in background o utilizzare AsyncTask per eseguire la transazione di rete su un thread in background.

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


76
  1. Non utilizzare strictMode (solo in modalità debug)
  2. Non modificare la versione dell'SDK
  3. Non utilizzare un thread separato

Usa Service o AsyncTask

Vedi anche la domanda Stack Overflow:

android.os.NetworkOnMainThreadException inviando un'e-mail da Android


8
Forse vale la pena sottolineare il fatto che se si utilizza un servizio sarà comunque necessario creare un thread separato: i callback del servizio vengono eseguiti sul thread principale. Un IntentService, d'altra parte, esegue il suo metodo onHandleIntent su un thread in background.
Stevie,

non dovresti usare un AsyncTask per operazioni di lunga durata! Le linee guida specificano da 2 a 3 secondi max.
Dage

76

Esegui 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 aggiungi questo ad AndroidManifest.xml

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

5
Ma come possiamo scoprire quando il thread finisce in questo in modo da poter eseguire il prossimo set di attività nel thread dell'interfaccia utente? AsyncTask offre la possibilità di farlo. C'è un modo per fare lo stesso usando thread eseguibili?
Piyush Soni,


1
È possibile utilizzare l'attività asincrona o il servizio di intenti, poiché viene eseguito sul thread di lavoro.
Chetan Chaudhari,

63

Si disabilita la modalità rigorosa utilizzando 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 : utilizzare l' AsyncTaskinterfaccia.

Codice completo per entrambi i metodi


2
Sì, sarebbe arrivato un errore ANR. significa che l'app non risponde entro 5 sec.
Muhammad Mubashir,

12
Questa è una risposta davvero negativa. Non dovresti cambiare la politica del thread ma scrivere un codice migliore: non eseguire operazioni di rete sul thread principale!
shkschneider,

@Sandeep Anche tu e gli altri spettatori dovreste leggere questo. stackoverflow.com/a/18335031/3470479
Prakhar1001

53

Le operazioni basate sulla rete non possono essere eseguite sul thread principale. È necessario eseguire tutte le attività basate sulla rete su un thread figlio o implementare AsyncTask.

Ecco come eseguire un'attività in un thread figlio:

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

1
Il Runnable anonimo NON è il modo migliore, dal momento che ha un riferimento implicito alla classe che lo racchiude e impedisce che venga editato da GC fino al completamento del thread! Anche questo thread verrà eseguito alla stessa priorità del thread principale / USA, in contrapposizione ai metodi del ciclo di vita e ai frame rate dell'interfaccia utente!
Yousha Aleayoub,

49

Inserisci il tuo codice all'interno:

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

O:

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
    }
}

Il secondo sarà il migliore del primo per API sopra l'11
Rohit Goswami,

46

Questo accade in Android 3.0 e versioni successive. Da Android 3.0 e versioni successive, hanno limitato l'utilizzo delle operazioni di rete (funzioni che accedono a Internet) dall'esecuzione nel thread principale / thread dell'interfaccia utente (ciò che viene generato dal tuo metodo di creazione e di ripresa dell'attività).

Questo per incoraggiare l'uso di thread separati per le operazioni di rete. Vedi AsyncTask per maggiori dettagli su come eseguire le attività di rete nel modo giusto.


46

L'uso di Annotazioni Android è un'opzione. Ti permetterà di eseguire semplicemente qualsiasi metodo in un thread in background:

// normal method
private void normal() {
    doSomething(); // do something in background
}

@Background
protected void doSomething() 
    // run your networking code here
}

Si noti che, sebbene offra vantaggi di semplicità e leggibilità, presenta i suoi svantaggi.


6
@Gavriel crea duplicati di tutto ciò che annoti, che si tratti di un metodo, attività, frammento, singleton ecc., Quindi c'è il doppio del codice e ci vuole più tempo per compilarlo. Potrebbe anche avere alcuni problemi a causa di bug nella libreria. Il debug e la ricerca di errori diventerebbe più difficile.
Oleksiy,

43

L'errore è dovuto all'esecuzione di operazioni di lunga durata nel thread principale. È possibile correggere facilmente il problema utilizzando AsynTask o Thread . Puoi controllare questa libreria AsyncHTTPClient per una migliore gestione.

AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {

    @Override
    public void onStart() {
        // Called before a request is started
    }

    @Override
    public void onSuccess(int statusCode, Header[] headers, byte[] response) {
        // Called when response HTTP status is "200 OK"
    }

    @Override
    public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
        // Called when response HTTP status is "4XX" (for example, 401, 403, 404)
    }

    @Override
    public void onRetry(int retryNo) {
        // Called when request is retried
    }
});

42

Non è necessario eseguire attività che richiedono molto tempo sul thread principale (thread dell'interfaccia utente), come qualsiasi operazione di rete, I / O di file o operazioni del database SQLite. Pertanto, per questo tipo di operazione, è necessario creare un thread di lavoro, ma il problema è che non è possibile eseguire direttamente alcuna operazione relativa all'interfaccia utente dal thread di lavoro. Per questo, devi usare Handlere passare il Message.

Per semplificare tutte queste cose, Android fornisce vari modi, come AsyncTask, AsyncTaskLoader, CursorLoadero IntentService. Quindi puoi utilizzare uno di questi in base alle tue esigenze.


40

La risposta migliore di spektom funziona perfettamente.

Se stai scrivendo l' AsyncTaskinline e non estendendoti come una classe, e soprattutto, se c'è bisogno di ottenere una risposta da AsyncTask, puoi usare il get()metodo come sotto.

RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();

(Dal suo esempio.)


5
usare get()è una cattiva idea ... fa "sincronizzare" nuovamente AsyncTask
Selvin

Esiste una via d'uscita migliore? @Selvin
sivag1

2
Penso che potresti informare il thread principale sul risultato. Ad esempio, invia una trasmissione al thread principale incluso il risultato.
Chine Gary,

32

Questo viene generato solo per le applicazioni destinate a Honeycomb SDK o versioni successive. Le applicazioni destinate a versioni precedenti dell'SDK possono eseguire attività di rete sui thread dei loop degli eventi principali.

L'errore è l'avvertimento SDK!


28

Per me è stato questo:

<uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10" />

Il dispositivo su cui stavo testando la mia app era la 4.1.2, che è SDK versione 16!

Assicurati che la versione di destinazione sia la stessa della tua Libreria di destinazione Android. Se non sei sicuro di quale sia la tua libreria di destinazione, fai clic con il pulsante destro del mouse su Progetto -> Percorso di costruzione -> Android e dovrebbe essere quello selezionato.

Inoltre, come altri hanno già detto, includono le autorizzazioni corrette per accedere a Internet:

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

11
Lascia che ti spieghi cosa stai facendo qui: NetworkOnMainThreadExceptionè il Guardian che ti sta dicendo: non sparare ai tuoi piedi ... la tua soluzione è: torniamo al passato quando non c'era Guardian - ora posso sparare al mio piede libero
Selvin,

1
Anch'io ho adottato questo approccio e non ho avuto problemi. Il guardiano è troppo pignolo a volte.
FractalBob,

25

Usa questo nella tua attività

    btnsub.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    //Initialize soap request + add parameters
                    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);

                    //Use this to add parameters
                    request.addProperty("pincode", txtpincode.getText().toString());
                    request.addProperty("bg", bloodgroup.getSelectedItem().toString());

                    //Declare the version of the SOAP request
                    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

                    envelope.setOutputSoapObject(request);
                    envelope.dotNet = true;

                    try {
                        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

                        //this is the actual part that will call the webservice
                        androidHttpTransport.call(SOAP_ACTION1, envelope);

                        // Get the SoapResult from the envelope body.
                        SoapObject result = (SoapObject) envelope.getResponse();
                        Log.e("result data", "data" + result);
                        SoapObject root = (SoapObject) result.getProperty(0);
                        // SoapObject s_deals = (SoapObject) root.getProperty(0);
                        // SoapObject s_deals_1 = (SoapObject) s_deals.getProperty(0);
                        //

                        System.out.println("********Count : " + root.getPropertyCount());

                        value = new ArrayList<Detailinfo>();

                        for (int i = 0; i < root.getPropertyCount(); i++) {
                            SoapObject s_deals = (SoapObject) root.getProperty(i);
                            Detailinfo info = new Detailinfo();

                            info.setFirstName(s_deals.getProperty("Firstname").toString());
                            info.setLastName(s_deals.getProperty("Lastname").toString());
                            info.setDOB(s_deals.getProperty("DOB").toString());
                            info.setGender(s_deals.getProperty("Gender").toString());
                            info.setAddress(s_deals.getProperty("Address").toString());
                            info.setCity(s_deals.getProperty("City").toString());
                            info.setState(s_deals.getProperty("State").toString());
                            info.setPinecode(s_deals.getProperty("Pinecode").toString());
                            info.setMobile(s_deals.getProperty("Mobile").toString());
                            info.setEmail(s_deals.getProperty("Email").toString());
                            info.setBloodgroup(s_deals.getProperty("Bloodgroup").toString());
                            info.setAdddate(s_deals.getProperty("Adddate").toString());
                            info.setWaight(s_deals.getProperty("waight").toString());
                            value.add(info);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Intent intent = new Intent(getApplicationContext(), ComposeMail.class);
                    //intent.putParcelableArrayListExtra("valuesList", value);

                    startActivity(intent);
                }
            }).start();
        }
    });

23

Solo per precisare qualcosa in modo esplicito:

Il thread principale è sostanzialmente il thread dell'interfaccia utente.

Pertanto, dire che non è possibile eseguire operazioni di rete nel thread principale significa che non è possibile eseguire operazioni di rete nel thread dell'interfaccia utente, il che significa che non è possibile eseguire operazioni di rete in un *runOnUiThread(new Runnable() { ... }*blocco all'interno di altri thread.

(Ho appena avuto un lungo momento di grattarsi la testa cercando di capire perché stavo ricevendo quell'errore in un posto diverso dal mio thread principale. Ecco perché; questo thread ha aiutato; e spero che questo commento aiuterà qualcun altro.)


22

Questa eccezione si verifica a causa di qualsiasi attività pesante eseguita sul thread principale se tale attività di esecuzione richiede troppo tempo .

Per evitarlo, possiamo gestirlo usando thread o esecutori

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});

18

Ci sono già molte grandi risposte su questa domanda, ma molte grandi biblioteche sono uscite da quando queste risposte sono state pubblicate. Questo è inteso come una specie di guida per principianti.

Tratterò diversi casi d'uso per eseguire operazioni di rete e una soluzione o due per ciascuno.

ReST su HTTP

Tipicamente Json, può essere XML o qualcos'altro

Accesso API completo

Supponiamo che tu stia scrivendo un'app che consente agli utenti di tenere traccia dei prezzi delle azioni, dei tassi di interesse e dei tassi di cambio. Trovi un'API Json simile a questa:

http://api.example.com/stocks                       //ResponseWrapper<String> object containing a list of Srings with ticker symbols
http://api.example.com/stocks/$symbol               //Stock object
http://api.example.com/stocks/$symbol/prices        //PriceHistory<Stock> object
http://api.example.com/currencies                   //ResponseWrapper<String> object containing a list of currency abbreviation
http://api.example.com/currencies/$currency         //Currency object
http://api.example.com/currencies/$id1/values/$id2  //PriceHistory<Currency> object comparing the prices of the first currency (id1) to the second (id2)

Retrofit da Square

Questa è una scelta eccellente per un'API con più endpoint e consente di dichiarare gli endpoint ReST invece di doverli codificare singolarmente come con altre librerie come ion o Volley. (sito web: http://square.github.io/retrofit/ )

Come lo usi con l'API delle finanze?

build.gradle

Aggiungi queste righe al tuo buid.gradle a livello di modulo:

implementation 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit library, current as of September 21, 2017
implementation 'com.squareup.retrofit2:converter-gson:2.3.0' //gson serialization and deserialization support for retrofit, version must match retrofit version

FinancesApi.java

public interface FinancesApi {
    @GET("stocks")
    Call<ResponseWrapper<String>> listStocks();
    @GET("stocks/{symbol}")
    Call<Stock> getStock(@Path("symbol")String tickerSymbol);
    @GET("stocks/{symbol}/prices")
    Call<PriceHistory<Stock>> getPriceHistory(@Path("symbol")String tickerSymbol);

    @GET("currencies")
    Call<ResponseWrapper<String>> listCurrencies();
    @GET("currencies/{symbol}")
    Call<Currency> getCurrency(@Path("symbol")String currencySymbol);
    @GET("currencies/{symbol}/values/{compare_symbol}")
    Call<PriceHistory<Currency>> getComparativeHistory(@Path("symbol")String currency, @Path("compare_symbol")String currencyToPriceAgainst);
}

FinancesApiBuilder

public class FinancesApiBuilder {
    public static FinancesApi build(String baseUrl){
        return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(FinancesApi.class);
    }
}

Finanze Frammento di frammento

FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior
api.getStock("INTC").enqueue(new Callback<Stock>(){
    @Override
    public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){
        Stock stock = stockCall.body();
        //do something with the stock
    }
    @Override
    public void onResponse(Call<Stock> stockCall, Throwable t){
        //something bad happened
    }
}

Se la tua API richiede l'invio di una chiave API o altra intestazione come un token utente, ecc., Retrofit rende tutto più semplice (vedi questa fantastica risposta per i dettagli: https://stackoverflow.com/a/42899766/1024412 ).

Accesso API ReST unico

Supponiamo che tu stia costruendo un'app "maltempo" che cerca la posizione GPS degli utenti e controlla la temperatura corrente in quella zona e dice loro l'umore. Questo tipo di app non deve dichiarare endpoint API; deve solo essere in grado di accedere a un endpoint API.

ione

Questa è un'ottima libreria per questo tipo di accesso.

Si prega di leggere la grande risposta di msysmilu ( https://stackoverflow.com/a/28559884/1024412 )

Carica immagini tramite HTTP

raffica

Volley può essere utilizzato anche per le API ReST, ma a causa della configurazione più complicata richiesta preferisco usare Retrofit da Square come sopra ( http://square.github.io/retrofit/ )

Supponiamo che tu stia creando un'app di social network e desideri caricare le foto del profilo degli amici.

build.gradle

Aggiungi questa riga al tuo buid.gradle a livello di modulo:

implementation 'com.android.volley:volley:1.0.0'

ImageFetch.java

Volley richiede più impostazioni rispetto a Retrofit. Dovrai creare una classe come questa per impostare RequestQueue, ImageLoader e ImageCache, ma non è poi così male:

public class ImageFetch {
    private static ImageLoader imageLoader = null;
    private static RequestQueue imageQueue = null;

    public static ImageLoader getImageLoader(Context ctx){
        if(imageLoader == null){
            if(imageQueue == null){
                imageQueue = Volley.newRequestQueue(ctx.getApplicationContext());
            }
            imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() {
                Map<String, Bitmap> cache = new HashMap<String, Bitmap>();
                @Override
                public Bitmap getBitmap(String url) {
                    return cache.get(url);
                }
                @Override
                public void putBitmap(String url, Bitmap bitmap) {
                    cache.put(url, bitmap);
                }
            });
        }
        return imageLoader;
    }
}

user_view_dialog.xml

Aggiungi quanto segue al tuo file xml di layout per aggiungere un'immagine:

<com.android.volley.toolbox.NetworkImageView
    android:id="@+id/profile_picture"
    android:layout_width="32dp"
    android:layout_height="32dp"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    app:srcCompat="@android:drawable/spinner_background"/>

UserViewDialog.java

Aggiungi il seguente codice al metodo onCreate (Frammento, Attività) o al costruttore (Finestra di dialogo):

NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());

Picasso

Un'altra eccellente biblioteca di Square. Si prega di consultare il sito per alcuni grandi esempi: http://square.github.io/picasso/


16

In parole semplici,

NON FUNZIONARE IN RETE NEL FILO DELL'UI

Ad esempio, se si esegue una richiesta HTTP, si tratta di un'azione di rete.

Soluzione:

  1. Devi creare una nuova discussione
  2. Oppure usa la classe AsyncTask

Modo:

Metti dentro tutte le tue opere

  1. run() metodo di nuova discussione
  2. O doInBackground() metodo della classe AsyncTask.

Ma:

Quando ricevi qualcosa dalla risposta di rete e vuoi mostrarla nella tua vista (come visualizzare il messaggio di risposta in TextView), devi tornare al thread dell'interfaccia utente .

Se non lo fai, otterrai ViewRootImpl$CalledFromWrongThreadException.

Come?

  1. Durante l'utilizzo di AsyncTask, aggiorna la vista dal onPostExecute()metodo
  2. Oppure chiama il runOnUiThread()metodo e aggiorna la vista all'interno del run()metodo.

12

È possibile spostare una parte del codice in un altro thread per scaricare il file main threaded evitare di ottenere ANR , NetworkOnMainThreadException , IllegalStateException (ad es. Impossibile accedere al database sul thread principale poiché potrebbe potenzialmente bloccare l'interfaccia utente per un lungo periodo di tempo).

Ci sono alcuni approcci che dovresti scegliere dipende dalla situazione

Discussione Java o Gestore Android

I thread Java sono monouso e muoiono dopo aver eseguito il metodo run.

HandlerThread è una classe utile per l'avvio di un nuovo thread con un looper.

AsyncTask

AsyncTask è progettato per essere una classe di supporto per Thread e Handler e non costituisce un framework di threading generico. AsyncTasks dovrebbe essere idealmente usato per operazioni brevi (al massimo pochi secondi). Se è necessario mantenere i thread in esecuzione per lunghi periodi di tempo, si consiglia vivamente di utilizzare le varie API fornite dal pacchetto java.util.concurrent come Executor , ThreadPoolExecutor e FutureTask .

Implementazione del pool di thread ThreadPoolExecutor , ScheduledThreadPoolExecutor ...

Classe ThreadPoolExecutor che implementa ExecutorService che offre un controllo accurato sul pool di thread (ad esempio, dimensione del pool principale, dimensione massima del pool, tempo di vita attivo, ecc.)

ScheduledThreadPoolExecutor - una classe che estende ThreadPoolExecutor. Può pianificare le attività dopo un determinato ritardo o periodicamente.

FutureTask

FutureTask esegue l'elaborazione asincrona, tuttavia, se il risultato non è ancora pronto o l'elaborazione non è completa, la chiamata a get () bloccherà il thread

AsyncTaskLoaders

AsyncTaskLoaders in quanto risolvono molti problemi inerenti ad AsyncTask

IntentService

Questa è la scelta defacto per l'elaborazione a lungo termine su Android, un buon esempio sarebbe quello di caricare o scaricare file di grandi dimensioni. Il caricamento e il download possono continuare anche se l'utente esce dall'app e certamente non si desidera impedire all'utente di poter utilizzare l'app mentre queste attività sono in corso.

JobScheduler

In effetti, è necessario creare un servizio e creare un lavoro utilizzando JobInfo.Builder che specifica i criteri per quando eseguire il servizio.

RxJava

Libreria per la composizione di programmi asincroni e basati su eventi utilizzando sequenze osservabili.

Coroutine (Kotlin)

L'essenziale è che rende il codice asincrono molto simile al sincrono

Leggi di più qui , qui , qui , qui


Ha funzionato per me ... Ho usato ampiamente AsyncTask, ma quando un'attività è in esecuzione un'altra aspetterà. Voglio risolvere questo. Ora lavora con executeonexecutor. Vediamo come si comporterà su dispositivi a memoria insufficiente.
Suraj Shingade,

Dai un'occhiata al metodo: asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, params); per eseguire il tuo compito contemporaneamente
yoAlex5

10

Anche se sopra c'è un enorme pool di soluzioni, nessuno ha menzionato com.koushikdutta.ion: https://github.com/koush/ion

È anche asincrono e molto semplice da usare:

Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
   @Override
    public void onCompleted(Exception e, JsonObject result) {
        // do stuff with the result or error
    }
});

10

Le soluzioni nuove Threade AsyncTask sono già state spiegate.

AsyncTaskdovrebbe essere idealmente usato per operazioni brevi. Normale Threadnon è preferibile per Android.

Dai un'occhiata alla soluzione alternativa utilizzando HandlerThread e Handler

HandlerThread

Handy class per l'avvio di un nuovo thread con un looper. Il looper può quindi essere utilizzato per creare classi di gestori. Si noti che start()deve ancora essere chiamato.

handler:

Un gestore consente di inviare ed elaborare oggetti Message e Runnable associati a MessageQueue di un thread. Ogni istanza del gestore è associata a un singolo thread e alla coda dei messaggi di quel thread. Quando si crea un nuovo gestore, viene associato alla coda thread / messaggi del thread che lo sta creando; da quel momento in poi, consegnerà i messaggi e i runnable a quella coda messaggi ed eseguirli man mano che escono dal messaggio coda.

Soluzione:

  1. Creare HandlerThread

  2. Chiamata start()suHandlerThread

  3. Crea Handlerottenendo LooperdaHanlerThread

  4. Incorporare Runnablenell'oggetto il codice relativo alle operazioni di rete

  5. Invia Runnableattività aHandler

Snippet di codice di esempio, quale indirizzo NetworkOnMainThreadException

HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
handler mainHandler = new Handler(handlerThread.getLooper());

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Ravi", "Before IO call");
            URL page = new URL("http://www.google.com");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ( (line =  buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Ravi", "After IO call");
            Log.d("Ravi",text.toString());

        }catch( Exception err){
            err.printStackTrace();
        }
    }
};
mainHandler.post(myRunnable);

Pro dell'utilizzo di questo approccio:

  1. La creazione di nuovi Thread/AsyncTaskper ogni operazione di rete è costosa. La Thread/AsyncTasksaranno distrutti e ricreati per i prossimi operazioni di rete. Ma con Handlere HandlerThreadapproccio, è possibile inviare molte operazioni di rete (come attività eseguibili) a single HandlerThreadusando Handler.

8

RxAndroidè un'altra alternativa migliore a questo problema e ci salva dalle seccature della creazione di thread e della pubblicazione di risultati sul thread dell'interfaccia utente Android. Dobbiamo solo specificare i thread su quali attività devono essere eseguite e tutto viene gestito internamente.

Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() { 

  @Override 
  public List<String> call() { 
    return mRestClient.getFavoriteMusicShows(); 
  }
});

mMusicShowSubscription = musicShowsObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {

    @Override 
    public void onCompleted() { }

    @Override 
    public void onError(Throwable e) { }

    @Override 
    public void onNext(List<String> musicShows){
        listMusicShows(musicShows);
    }
});
  1. Da specifiying (Schedulers.io()), RxAndroid verrà eseguito getFavoriteMusicShows() in un thread diverso.

  2. Usando AndroidSchedulers.mainThread()vogliamo osservare questo osservabile sul thread dell'interfaccia utente, ovvero vogliamo che il nostro onNext()callback venga chiamato sul thread dell'interfaccia utente


8

Il thread principale è il thread dell'interfaccia utente e non è possibile eseguire un'operazione nel thread principale che potrebbe bloccare l'interazione dell'utente. Puoi risolverlo in due modi:

Forza in questo modo l'attività nel thread principale

StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(threadPolicy);

Oppure crea un semplice gestore e aggiorna il thread principale, se lo desideri.

Runnable runnable;
Handler newHandler;

newHandler = new Handler();
runnable = new Runnable() {
    @Override
    public void run() {
         try {
            //update UI
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
};
newHandler.post(runnable);

E per interrompere il thread utilizzare:

newHandler.removeCallbacks(runnable);

Per ulteriori informazioni, dai un'occhiata a: Threading indolore


Grazie. La versione 1 aiuta quando si aggiunge come prima azione in onCreate.
Ingo,

7

Questo funziona Ho appena reso la risposta del Dr. Liuiji un po 'più semplice.

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

7

Su Android, le operazioni di rete non possono essere eseguite sul thread principale. È possibile utilizzare Thread, AsyncTask (attività di breve durata), Servizio (attività di lunga durata) per eseguire operazioni di rete.


7

L'accesso alle risorse di rete dal thread principale (UI) causa questa eccezione. Utilizzare un thread separato o AsyncTask per accedere a una risorsa di rete per evitare questo problema.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.