2475 votes

android.os.NetworkOnMainThreadException

Dans le code ci-dessous j'ai une erreur lors de l'exécution de mon projet Android pour les fils rss.

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 affiche une erreur:

android.os.NetworkOnMainThreadException

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

2600voto

spektom Points 11130

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

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

Comment faire pour exécuter la tâche:

new RetrieveFeedTask().execute(urlToRssFeed);

N'oubliez pas de l'ajouter à AndroidManifest.xml le fichier:

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

708voto

user1169115 Points 2679

Je pense que android.os.NetworkOnMainThreadException Solution de contournement est....

Ajouter:

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

StrictMode.setThreadPolicy(policy); 

Dans votre classe,

et

AJOUTER cette autorisation dans android manifest.xml fichier:

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

447voto

Dr.Luiji Points 1595

- Je résoudre ce problème en utilisant un nouveau Thread

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

thread.start(); 

179voto

Stevie Points 1515

Il existe plusieurs alternatives pour la accepté de répondre, chacun avec différents arbitrages. Permettez-moi de commencer par dire que l'on a accepté la réponse est bonne, et moi-voter, mais il n'est pas la seule et elle a quelques bas-côtés:

  • AsyncTask est créée comme non-statique à l'intérieur des classes ont une référence implicite à l'encadrement de l'Activité de l'objet, de son contexte, et la Vue d'ensemble de la hiérarchie créée par cette activité. Cette référence empêche l'Activité de déchets collectés jusqu'à l'AsyncTask du travail de fond complète. Si la connexion de l'utilisateur est lent, et/ou le téléchargement est grand, ces court-terme des fuites de mémoire peut devenir un problème, si par exemple, les changements d'orientation plusieurs fois (et vous n'avez pas annuler l'exécution de tâches), ou que l'utilisateur accède à l'écart de l'Activité.
  • AsyncTask est différent de l'exécution des caractéristiques dépendant de la plate-forme s'exécute sur: avant API de niveau 4 AsyncTasks exécuter en série sur un seul thread d'arrière-plan; à partir de l'API de niveau 4 par le biais de l'API de niveau 10, AsyncTasks exécuter sur un bassin allant jusqu'à 128 threads; à partir de l'API de niveau 11, AsyncTask exécute en série sur un seul thread d'arrière-plan (sauf si vous utilisez la surcharge executeOnExecutor méthode et offre une alternative exécuteur testamentaire). Code qui fonctionne très bien lorsqu'il est exécuté en série sur ICS peuvent se briser lorsqu'elles sont exécutées simultanément sur pain d'épice, disons, si vous avez par inadvertance ordre d'exécution des dépendances.

Si vous voulez éviter de court-terme des fuites de mémoire, ont bien défini exécution caractéristiques à travers toutes les plateformes, et avoir une base pour construire vraiment solide réseau de manutention, vous pourriez envisager:

  1. À l'aide d'une bibliothèque qui fait un excellent travail pour vous - il y a une belle comparaison de la mise en réseau libs dans cette question, ou
  2. À l'aide d'un Service ou IntentService au lieu de cela, peut-être avec un PendingIntent de renvoyer le résultat via l'Activité, en onActivityResult méthode.

IntentService approche

Les bas-côtés:

  • Plus de code et de complexité qu' AsyncTask, bien que pas autant que vous pourriez le penser
  • Aura la file d'attente de demandes et de les exécuter sur un seul thread d'arrière-plan. Vous pouvez facilement contrôler ce problème en remplaçant IntentService avec un équivalent Service mise en œuvre, peut-être comme cette une.
  • Euh, je ne peux pas penser à d'autres le droit maintenant en fait

Jusqu'à-côtés:

  • Évite le court terme, problème de fuite de mémoire
  • Si votre activité redémarre alors que les opérations de réseau sont en vol, il peut toujours recevoir le résultat de la télécharger via son onActivityResult méthode
  • Meilleure plate-forme que les AsyncTask de construire et de ré-utilisation robuste code réseau. Exemple: si vous avez besoin de faire une importante télécharger, vous pouvez le faire à partir d' AsyncTask en Activity, mais si le contexte de l'utilisateur-permet de sortir de l'application pour prendre un appel, le système peut tuer l'application avant que le téléchargement se termine. Il est moins susceptible de tuer une application avec un actif Service.
  • Si vous utilisez votre propre version simultanée d' IntentService (comme celle que j'ai lié ci-dessus), vous pouvez contrôler le niveau de la concurrence via l' Executor.

La mise en œuvre résumé

Vous pouvez mettre en œuvre un IntentService pour 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 dire ce qui est à télécharger via Intent supplémentaires, et de passer à un PendingIntent d'utilisation de retourner le résultat de l' 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: Inscrivez-vous au service dans le manifeste:

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

Étape 3: Appeler le service à partir de l'Activité, le passage d'un PendingResult objet dont le Service va utiliser 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 travail complet Android-Studio/gradle projet est disponible ici.

147voto

Mark Allison Points 10123

Vous ne pouvez pas effectuer réseau I/O sur le thread d'INTERFACE utilisateur sur Nid d'abeille. Techniquement, il est possible que sur les versions précédentes d'Android, mais c'est vraiment une mauvaise idée car il sera la cause de votre application cesse de répondre, et peut entraîner l'OS de tuer votre application d'être mal comportés. Vous aurez besoin de lancer un processus en arrière-plan ou de l'utilisation AsyncTask pour effectuer votre transaction réseau sur un thread d'arrière-plan.

Il y a un article sur Indolore Filetage sur l'Android developer site qui est une bonne introduction à cela, et il vous donnera une bien meilleure profondeur de la réponse que peut raisonnablement être fournies ici.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X