¿Cómo puedo solucionar 'android.os.NetworkOnMainThreadException'?

Resuelto bejoy george asked hace 54 años • 64 respuestas

Recibí 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?

bejoy george avatar Jan 01 '70 08:01 bejoy george
Aceptado

NOTA: AsyncTask quedó obsoleto en el nivel de API 30.
AsyncTask | Desarrolladores de Android

Esta excepción se produce cuando una aplicación intenta realizar una operación de red en su hilo principal. Ejecute su 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.javael archivo puedes agregar esta línea dentro de tu oncreate()método.

new RetrieveFeedTask().execute(urlToRssFeed);

No olvides agregar esto al AndroidManifest.xmlarchivo:

<uses-permission android:name="android.permission.INTERNET"/>
Michael Spector avatar Jun 14 '2011 12:06 Michael Spector

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

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

Agregar:

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

StrictMode.setThreadPolicy(policy);

En tu clase,

y

Agregue este permiso en el archivo manifest.xml de Android :

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

Consecuencias:

Su aplicación (en áreas de conexión a Internet irregular) dejará de responder y se bloqueará, el usuario percibirá lentitud y tendrá que realizar una eliminación forzada, y usted corre el riesgo de que el administrador de actividad elimine su aplicación y le diga al usuario que se ha detenido.

Android tiene algunos buenos consejos sobre buenas prácticas de programación para diseñar con capacidad de respuesta: NetworkOnMainThreadException | Desarrolladores de Android

user1169115 avatar Feb 15 '2012 06:02 user1169115

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

        
Dr.Luiji avatar Jan 21 '2013 16:01 Dr.Luiji

La respuesta aceptada tiene algunas desventajas importantes. No es recomendable utilizar AsyncTask para redes a menos que realmente sepas lo que estás haciendo. Algunas de las desventajas incluyen:

  • Las AsyncTask creadas como clases internas no estáticas tienen una referencia implícita al objeto Actividad adjunto, su contexto y toda la jerarquía de Vistas creada por esa actividad. Esta referencia evita que la actividad se recopile como basura hasta que se complete el trabajo en segundo plano de 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 en ejecución), o el usuario navega fuera de la Actividad.
  • AsyncTask tiene diferentes características de ejecución según la plataforma en la que se ejecuta: antes del nivel API 4, AsyncTasks se ejecuta en serie en un único subproceso en segundo plano; desde el nivel de API 4 hasta el nivel de API 10, AsyncTasks se ejecuta en un grupo de hasta 128 subprocesos; desde el nivel API 11 en adelante, AsyncTask se ejecuta en serie en un único subproceso en segundo plano (a menos que utilice el executeOnExecutormétodo sobrecargado y proporcione un ejecutor alternativo). El código que funciona bien cuando se ejecuta en serie en ICS puede fallar cuando se ejecuta simultáneamente en Gingerbread, por ejemplo, si tiene dependencias inadvertidas en el orden de ejecución.

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

  1. Usar una biblioteca que haga un buen trabajo para usted; hay una buena comparación de bibliotecas de redes en esta pregunta , o
  2. Usando a Serviceo IntentServiceen su lugar, tal vez con a PendingIntentpara devolver el resultado a través del método de Actividad onActivityResult.

Enfoque de servicio de intención

Desventajas:

  • Más código y complejidad que AsyncTask, aunque no tanto como podría pensar
  • Pondrá en cola las solicitudes y las ejecutará en un único hilo en segundo plano. Puede controlar esto fácilmente reemplazándolo IntentServicecon una implementación equivalente Service, tal vez como esta .
  • Um, no puedo pensar en ningún otro en este momento en realidad.

Ventajas:

  • Evita el problema de pérdida de memoria a corto plazo.
  • Si su actividad se reinicia mientras las operaciones de red están en curso, aún puede recibir el resultado de la descarga a través de su onActivityResultmétodo .
  • Una plataforma mejor que AsyncTask para crear y reutilizar código de red sólido. Ejemplo: si necesita realizar una carga importante, puede hacerlo desde AsyncTaskun archivo Activity, pero si el usuario sale de la aplicación para atender una llamada telefónica, el sistema puede cerrar la aplicación antes de que se complete la carga. Es menos probable que cierre una aplicación con un archivo Service.
  • Si usa su propia versión concurrente IntentService(como la que vinculé anteriormente), puede controlar el nivel de concurrencia a través del archivo Executor.

Resumen de implementación

Puede implementar una función IntentServicepara realizar descargas en un único hilo en segundo plano con bastante facilidad.

Paso 1: Crea un IntentServicepara realizar la descarga. Puede decirle qué descargar a través de Intentextras y pasarle un PendingIntentuso para devolver el resultado 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 reuse, 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: Registre el servicio en el manifiesto:

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

Paso 3: Invocar el servicio desde 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: Manejar el resultado en 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 proyecto de GitHub que contiene un proyecto completo de Android Studio/ Gradle está disponible aquí .

Stevie avatar Jan 22 '2014 13:01 Stevie