Question Comment puis-je résoudre android.os.NetworkOnMainThreadException?


J'ai eu une erreur lors de l'exécution de mon projet Android pour RssReader.

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

Et il montre l'erreur ci-dessous:

android.os.NetworkOnMainThreadException

Comment puis-je résoudre ce problème?


1969
2018-06-14 12:02


origine


Réponses:


Cette exception est levée lorsqu'une application tente d'effectuer une opération de mise en réseau sur son thread principal. Exécutez votre code dans 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
    }
}

Comment exécuter la tâche:

Dans MainActivity.java fichier, vous pouvez ajouter cette ligne dans votre oncreate() méthode

new RetrieveFeedTask().execute(urlToRssFeed);

Ne pas oublier d'ajouter ceci à AndroidManifest.xml fichier:

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

2232
2018-06-14 12:15



Vous devriez presque toujours exécuter les opérations réseau sur un thread ou en tant que tâche asynchrone.

Mais il est possible de supprimer cette restriction et vous remplacez le comportement par défaut, si vous êtes prêt à accepter les conséquences.

Ajouter:

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

StrictMode.setThreadPolicy(policy); 

Dans votre classe,

et

AJOUTEZ cette autorisation dans le fichier android manifest.xml:

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

Conséquences:

Votre application (dans les zones de connexion Internet inégale) ne répond plus et se bloque, l'utilisateur perçoit la lenteur et doit faire une mise à mort forcée, et vous risquez que le gestionnaire d'activités tue votre application et indique à l'utilisateur que l'application a cessé.

Android a quelques bons conseils sur les bonnes pratiques de programmation à concevoir pour la réactivité: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


556
2018-02-15 06:59



J'ai résolu ce problème en utilisant une nouvelle 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



Vous ne pouvez pas effectuer de réseau I / O sur le fil de l'interface utilisateur sur Rayon de miel. Techniquement, il est possible sur les versions antérieures d'Android, mais c'est une très mauvaise idée, car cela va empêcher votre application de répondre, et le système d'exploitation peut tuer votre application pour qu'elle se comporte mal. Vous devrez exécuter un processus en arrière-plan ou utiliser AsyncTask pour effectuer votre transaction réseau sur un thread d'arrière-plan.

Il y a un article sur Filetage indolore sur le site développeur Android qui est une bonne introduction à cela, et il vous fournira une bien meilleure profondeur de réponse que ce qui peut être fourni de façon réaliste ici.


124
2018-06-14 12:07



La réponse acceptée a quelques inconvénients significatifs. Il est déconseillé d'utiliser AsyncTask pour la mise en réseau, sauf si vous vraiment Sais ce que tu fais. Certains des côtés bas incluent:

  • Les classes internes AsyncTask créées en tant que classes internes non statiques ont une référence implicite à l'objet Activity englobant, à son contexte et à l'ensemble de la hiérarchie View créée par cette activité. Cette référence empêche l'activité d'être récupérée avant la fin du travail en arrière-plan de la tâche asynchrone. Si la connexion de l'utilisateur est lente et / ou le téléchargement est important, ces fuites de mémoire à court terme peuvent devenir un problème - par exemple si l'orientation change plusieurs fois (et que vous n'annulez pas les tâches d'exécution) ou que l'utilisateur navigue loin de l'activité.
  • AsyncTask a des caractéristiques d'exécution différentes en fonction de la plate-forme sur laquelle il s'exécute: avant le niveau API 4, les AsyncTasks s'exécutent en série sur un seul thread d'arrière-plan; du niveau d'API 4 au niveau d'API 10, les AsyncTasks s'exécutent sur un pool de 128 threads maximum; à partir du niveau 11 de l'API AsyncTask s'exécute en série sur un thread d'arrière-plan unique (sauf si vous utilisez la surcharge executeOnExecutorméthode et fournir un exécuteur alternatif). Le code qui fonctionne correctement lorsqu'il est exécuté en série sur ICS peut être interrompu lorsqu'il est exécuté simultanément sur Gingerbread, par exemple, si vous avez des dépendances d'ordre d'exécution par inadvertance.

Si vous souhaitez éviter les fuites de mémoire à court terme, avoir des caractéristiques d'exécution bien définies sur toutes les plates-formes, et avoir une base pour construire une gestion réseau très robuste, vous pouvez envisager:

  1. En utilisant une bibliothèque qui fait un bon travail pour vous - il y a une bonne comparaison des bibliothèques de mise en réseau dans cette question, ou
  2. Utilisant un Service ou IntentService au lieu de cela, peut-être avec un PendingIntent renvoyer le résultat via l'activité onActivityResult méthode.

IntentService approche

Vers le bas:

  • Plus de code et de complexité que AsyncTask, mais pas autant que vous pourriez le penser
  • Va mettre en file d'attente les demandes et les exécuter sur un unique fil d'arrière-plan. Vous pouvez facilement contrôler cela en remplaçant IntentService avec un équivalent Service mise en œuvre, peut-être comme celui-là.
  • Euh, je ne peux pas penser à d'autres en ce moment

Vers le haut:

  • Évite le problème de fuite de mémoire à court terme
  • Si votre activité redémarre alors que les opérations réseau sont en cours, elle peut toujours recevoir le résultat du téléchargement via son onActivityResult méthode
  • Meilleure plate-forme qu'AsyncTask pour construire et réutiliser un code de réseau robuste. Exemple: si vous devez effectuer un téléchargement important, vous pouvez le faire depuis AsyncTask dans un Activity, mais si le contexte de l'utilisateur se déconnecte de l'application pour prendre un appel téléphonique, le système mai tuez l'application avant la fin du téléchargement. C'est moins probable pour tuer une application avec un actif Service.
  • Si vous utilisez votre propre version simultanée de IntentService (comme celui que j'ai lié ci-dessus), vous pouvez contrôler le niveau de concurrence via le Executor.

Résumé d'implémentation

Vous pouvez mettre en œuvre un IntentService effectuer des téléchargements sur un seul thread d'arrière-plan assez facilement.

Étape 1: Créer un IntentService pour effectuer le téléchargement. Vous pouvez le dire quoi télécharger via Intent extra, et passez-le PendingIntent à utiliser pour renvoyer le résultat à la 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);
        }
    }
}

Étape 2: Enregistrez le service dans le manifeste:

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

Étape 3: appelez le service à partir de l'activité, en transmettant un objet PendingResult que le service utilisera pour renvoyer le résultat:

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

Étape 4: gérer le résultat dans 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 projet github contenant un projet Android-Studio / gradle complet est disponible ici.


118
2018-01-22 13:17



  1. N'utilisez pas strictMode (uniquement en mode débogage)
  2. Ne pas modifier la version SDK
  3. Ne pas utiliser un fil séparé

Utiliser le service ou AsyncTask

Voir aussi Stack Overflow question:

android.os.NetworkOnMainThreadException envoie un e-mail depuis Android


59
2017-08-18 09:18



Faire les actions de réseau sur un autre thread

Par exemple:

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

Et ajoutez ceci à AndroidManifest.xml

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

51
2017-12-24 14:33



Vous désactivez le mode strict en utilisant le code suivant:

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

Ceci n'est pas recommandé: Utilisez le AsyncTaskinterface.

Code complet pour les deux méthodes


46
2017-10-24 07:10



Les opérations réseau ne peuvent pas être exécutées sur le thread principal. Vous devez exécuter toutes les tâches réseau sur un thread enfant ou implémenter AsyncTask.

Voici comment vous exécutez une tâche dans un thread enfant:

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



Mettez votre code à l'intérieur:

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

Ou:

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