Pregunta ¿Cómo arreglo la excepción android.os.NetworkOnMainThreadException?


Obtuve un error al ejecutar mi proyecto de Android para RssReader.

Código:

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

Y muestra el siguiente error:

android.os.NetworkOnMainThreadException

¿Cómo puedo solucionar este problema?


1969
2018-06-14 12:02


origen


Respuestas:


Esta excepción se produce cuando una aplicación intenta realizar una operación de red en su hilo principal. Ejecuta tu código 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;
        } finally {
            is.close();
        }
    }

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

Cómo ejecutar la tarea:

En MainActivity.java archivo puede agregar esta línea dentro de su oncreate() método

new RetrieveFeedTask().execute(urlToRssFeed);

No olvides agregar esto a AndroidManifest.xml archivo:

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

2232
2018-06-14 12:15



Casi siempre debería ejecutar operaciones de red en un subproceso o como una tarea asincrónica.

Pero es Es posible eliminar esta restricción y usted anula el comportamiento predeterminado, si está dispuesto a aceptar las consecuencias.

Añadir:

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

StrictMode.setThreadPolicy(policy); 

En tu clase,

y

AGREGUE este permiso en el archivo android manifest.xml:

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

Consecuencias:

Su aplicación (en áreas de conexión irregular a Internet) no responde y se bloquea, el usuario percibe lentitud y tiene que matar forzosamente, y se arriesga a que el administrador de actividades mate su aplicación y le diga al usuario que la aplicación se ha detenido.

Android tiene algunos buenos consejos sobre buenas prácticas de programación para diseñar la capacidad de respuesta: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


556
2018-02-15 06:59



Resolví este problema usando un nuevo 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



No puedes realizar la red E / S en el hilo de la interfaz de usuario en Panal. Técnicamente, es posible en versiones anteriores de Android, pero es una muy mala idea, ya que hará que su aplicación deje de responder, y puede provocar que el sistema operativo mate su aplicación por comportarse mal. Tendrá que ejecutar un proceso en segundo plano o usar AsyncTask para realizar su transacción de red en una cadena de fondo.

Hay un artículo sobre Roscado indolora en el sitio para desarrolladores de Android, que es una buena introducción a esto, y le proporcionará una respuesta mucho más profunda de la que se puede proporcionar de forma realista aquí.


124
2018-06-14 12:07



La respuesta aceptada tiene algunas desventajas significativas. No es aconsejable utilizar AsyncTask para redes a menos que De Verdad sabe lo que está haciendo Algunos de los inconvenientes incluyen:

  • AsyncTask's creadas como clases internas no estáticas tienen una referencia implícita al objeto de actividad circundante, su contexto y toda la jerarquía de vistas creada por esa actividad. Esta referencia impide que la actividad se recolecte como basura hasta que se complete el trabajo en segundo plano de la tarea AsyncTask. Si la conexión del usuario es lenta y / o la descarga es grande, estas pérdidas de memoria a corto plazo pueden convertirse en un problema; por ejemplo, si la orientación cambia varias veces (y no cancela las tareas de ejecución), o si el usuario navega lejos de la actividad.
  • AsyncTask tiene diferentes características de ejecución dependiendo de la plataforma en la que se ejecuta: antes del nivel 4 de la API, AsyncTasks se ejecuta en serie en una única cadena de fondo; desde el nivel 4 de API hasta el nivel 10 de API, AsyncTasks se ejecuta en un grupo de hasta 128 subprocesos; desde el nivel 11 de API en adelante, AsyncTask se ejecuta en serie en una única cadena de fondo (a menos que use la sobrecarga executeOnExecutormétodo y suministrar un ejecutor alternativo). El código que funciona bien cuando se ejecuta en serie en ICS puede romperse cuando se ejecuta al mismo tiempo en Gingerbread, por ejemplo, si tiene dependencias inadvertidas de orden de ejecución.

Si desea evitar fugas de memoria a corto plazo, tener características de ejecución bien definidas en todas las plataformas y tener una base para desarrollar un manejo de red realmente robusto, es posible que desee considerar:

  1. Usando una biblioteca que hace un buen trabajo de esto para usted, hay una buena comparación de libs de red en esta pregunta, o
  2. Usando un Service o IntentService en cambio, tal vez con un PendingIntent para devolver el resultado a través de la Actividad onActivityResult método.

El enfoque de IntentService

Down-side:

  • Más código y complejidad que AsyncTask, aunque no tanto como podrías pensar
  • Pondrá en cola las solicitudes y las ejecutará en un soltero hilo de fondo. Usted puede controlar esto reemplazando IntentService con un equivalente Service implementación, tal vez como éste.
  • Um, no puedo pensar en otros ahora mismo en realidad

Aspectos positivos:

  • Evita el problema de fuga de memoria a corto plazo
  • Si su actividad se reinicia mientras las operaciones de la red están en vuelo, aún puede recibir el resultado de la descarga a través de su onActivityResult método
  • Mejor plataforma que AsyncTask para construir y reutilizar un código de red robusto. Ejemplo: si necesita hacer una carga importante, puede hacerlo desde AsyncTask en una Activity, pero si el contexto del usuario se desconecta de la aplicación para tomar una llamada telefónica, el sistema mayo matar la aplicación antes de que se complete la carga. Es menos probable para matar una aplicación con un activo Service.
  • Si usa su propia versión concurrente de IntentService (como el que he vinculado anteriormente) puede controlar el nivel de concurrencia a través del Executor.

Resumen de implementación

Puedes implementar un IntentService para realizar descargas en un solo hilo de fondo con bastante facilidad.

Paso 1: crea un IntentService para realizar la descarga. Puedes decirle qué descargar a través de Intent extra, y pasarlo a PendingIntent usar para devolver el resultado 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);
        }
    }
}

Paso 2: registrar el servicio en el manifiesto:

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

Paso 3: Invoque el servicio de la Actividad, pasando un objeto PendingResult que el Servicio utilizará para devolver el resultado:

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

Paso 4: maneja el resultado en el resultado de actividad:

@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 proyecto github que contiene un proyecto completo de Android-Studio / gradle está disponible aquí.


118
2018-01-22 13:17



  1. No use strictMode (solo en modo de depuración)
  2. No cambie la versión de SDK
  3. No use un hilo separado

Use Service o AsyncTask

Véase también Pregunta de desbordamiento de pila:

android.os.NetworkOnMainThreadException enviando un correo electrónico desde Android


59
2017-08-18 09:18



Haga las acciones de red en otro hilo

Por ejemplo:

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

Y agrégalo a AndroidManifest.xml

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

51
2017-12-24 14:33



Deshabilita el modo estricto usando el siguiente código:

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

Esto no es recomendado: utilizar el AsyncTaskinterfaz.

Código completo para ambos métodos


46
2017-10-24 07:10