¿Cómo arreglar 'android.os.NetworkOnMainThreadException'?

2395

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
fuente
131
Lea esta publicación de blog en NetworkOnMainThreadException para obtener más información. Explica por qué ocurre esto en Android 3.0 y superior.
Adrian Monk
66
Para estar en la pista del rito, primero lea sobre las solicitudes de red en Android y luego recomendaría estudiar "Volley".
Anuj Sharma el
3
Hay muchas bibliotecas alternativas que resuelven este problema. Muchos se enumeran al final de esta página . Si tienes más, los llevamos :)
Snicolas
Debe ejecutar actividades de Internet en un hilo separado del hilo principal (UI)
Naveed Ahmad
"Debido a un error en versiones anteriores de Android, el sistema no marcó la escritura en un socket TCP en el hilo principal como una violación de modo estricto. Android 7.0 corrige este error. Las aplicaciones que exhiben este comportamiento ahora arrojan un android.os. NetworkOnMainThreadException ". - ¡Así que algunos de nosotros no hemos tocado esto hasta hace poco! developer.android.com/about/versions/nougat/…
Jay

Respuestas:

2548

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 el MainActivity.javaarchivo puede agregar esta línea dentro de su oncreate()método

new RetrieveFeedTask().execute(urlToRssFeed);

No olvide agregar esto al AndroidManifest.xmlarchivo:

<uses-permission android:name="android.permission.INTERNET"/>
Michael Spector
fuente
37
Creo que vale la pena señalar aquí que el fragmento de código anterior se supone que es una subclase (clase interna), preferiblemente privada. De esa manera, cuando finalice AsyncTask, aún puede manipular las entrañas de su clase.
dyslexicanaboko
44
En realidad, hice lo mismo que mencionaste anteriormente, pero estoy
Dhruv Tyagi
68
Esta es exactamente la respuesta incorrecta. Me encuentro con esto todo el tiempo en el código de personas, y es molesto tener que arreglarlo todo el tiempo. AsyncTask no debe usarse para la actividad de red, porque está vinculado a la actividad, pero no al ciclo de vida de la actividad. Girar el dispositivo con esta tarea en ejecución provocará una excepción y bloqueará su aplicación. Utilice en su lugar un IntentService que elimine datos en la base de datos sqlite.
Brill Pappin
55
Precaución, AsyncTask se usa a menudo para operaciones de red por actividad cuando no debería serlo. Su ciclo de vida no está sincronizado con la actividad. Para obtener datos, debe usar un IntentService y la base de datos detrás de la vista.
Brill Pappin
1
@BrillPappin, FWIW, esta Guía para desarrolladores de Android utiliza AsyncTasky se encarga de un cambio de configuración.
HeyJude
678

Casi siempre debe ejecutar operaciones de red en un hilo 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.

Añadir:

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

StrictMode.setThreadPolicy(policy); 

En tu clase,

y

AGREGAR este permiso en el archivo android manifest.xml:    

<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 percibe la lentitud y tiene que matar a la fuerza, y corre el riesgo de 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 con capacidad de respuesta: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html

usuario1169115
fuente
456
Esta es una muy mala idea. la solución es evitar la E / S de red en el hilo principal (como muestra la respuesta aceptada)
MByD
74
Con esto solo ocultas tu verdadero problema.
Alex
28
@TwistedUmbrella AsyncTask no agrega una página de código, agrega 6 líneas (declaración de clase, anulación de anulación, doInBackgrounddeclaración, 2 corchetes de cierre y una llamada a execute()). Por otro lado, incluso una sola búsqueda de un sitio como usted menciona trae un retraso significativo en la capacidad de respuesta de la interfaz de usuario. No seas perezoso
Zoltán
19
Creo que esta es la solución perfecta si solo está buscando ejecutar un fragmento de código de muestra para ver si algo funciona antes de implementar la AsyncTask adecuada. Es por eso que voté por esta respuesta, aunque, como todos los demás han dicho, esto no debe hacerse en una aplicación de producción, solo como una solución rápida para una prueba de desarrollo.
enganchado82
94
Votado Esta respuesta es correcta, y para los muchos programadores que no son ni ingenuos ni estúpidos, pero que simplemente requieren una llamada SINCRÓNICA (es decir, esto debe bloquear la aplicación ), esto es exactamente lo que se necesita. Estoy más que feliz de que Android arroje la excepción por defecto (¡en mi humilde opinión es algo muy "útil"!), Pero estoy igualmente feliz de decir "gracias, pero, esto es lo que pretendía" y anular eso.
Adam
428

Resolví este problema usando uno 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
fuente
77
En lugar de crear un nuevo subproceso cada vez que desee realizar una operación de red, también podría usar un servicio de ejecución de un solo subproceso.
Alex Lockwood
66
Simple pero peligroso. El Runnable anónimo tiene una referencia implícita a la clase adjunta (por ejemplo, su Actividad o Fragmento), evitando que se recolecte basura hasta que se complete el subproceso. Al menos, debe establecer la prioridad en Process.BACKGROUND, de lo contrario, este hilo se ejecutará con la misma prioridad que el hilo principal / ui, compitiendo con los métodos de ciclo de vida y las tasas de cuadros ui (tenga cuidado con las advertencias en el registro del coreógrafo).
Stevie
1
@ Stevie cómo establecer la prioridad? ni runnble ni el ejecutorService tienen un método de establecimiento de este tipo
JK
1
@JK Proporcione a su ExecutorService un ThreadFactory personalizado y llame a Thread.setPriority en el hilo antes de devolverlo.
Stevie
1
"Se desaconseja el uso de subprocesos directamente en Android. Causa más problemas de los que resuelve" ¿Le importaría dar más detalles? En realidad, AsyncTask está siendo desaprobado exactamente por eso ... techyourchance.com/asynctask-deprecated
Fran Marzoa
170

La respuesta aceptada tiene algunas desventajas significativas. No es aconsejable usar AsyncTask para la creación de redes a menos que realmente sepa lo que está haciendo. Algunos de los inconvenientes incluyen:

  • Las tareas asíncronas creadas como clases internas no estáticas tienen una referencia implícita al objeto de actividad que lo encierra, su contexto y toda la jerarquía de vistas creada por esa actividad. Esta referencia evita que la actividad se recolecte basura hasta que se complete el trabajo de fondo 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 de ejecución), o el usuario navega lejos de la Actividad.
  • AsyncTask tiene diferentes características de ejecución según la plataforma en la que se ejecuta: antes del nivel 4 de API, las AsyncTasks se ejecutan en serie en un único subproceso en segundo plano; desde el nivel API 4 hasta el nivel API 10, AsyncTasks se ejecuta en un grupo de hasta 128 subprocesos; desde el nivel de 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 romperse cuando se ejecuta simultáneamente en Gingerbread, por ejemplo, si tiene dependencias involuntarias de 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 robusto, puede considerar:

  1. Usando una biblioteca que hace un buen trabajo para usted: hay una buena comparación de las bibliotecas de redes en esta pregunta , o
  2. Utilizando una Serviceo IntentServiceen su lugar, tal vez con un PendingIntenta devolver el resultado a través de la Actividad onActivityResultmétodo.

Enfoque de servicio

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 subproceso en segundo plano. Puede controlar esto fácilmente reemplazándolo IntentServicecon una Serviceimplementación equivalente , tal vez como esta .
  • Um, no puedo pensar en ningún otro ahora.

Alzas:

  • Evita el problema de pérdida de memoria a corto plazo
  • Si su actividad se reinicia mientras las operaciones de red están en vuelo, aún puede recibir el resultado de la descarga a través de su onActivityResultmétodo
  • Mejor plataforma que AsyncTask para construir y reutilizar código de red robusto. Ejemplo: si necesita realizar una carga importante, puede hacerlo desde AsyncTaskuna Activity, pero si el usuario cambia de contexto 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 mate una aplicación con un activo Service.
  • Si utiliza su propia versión simultánea de IntentService(como la que he vinculado anteriormente) puede controlar el nivel de concurrencia a través de Executor.

Resumen de implementación

Puede implementar un IntentServicepara realizar descargas en un solo hilo de fondo con bastante facilidad.

Paso 1: Crea una IntentServicepara realizar la descarga. Puede decirle qué descargar a través de Intentextra, y pasarlo PendingIntenta usar 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 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: Registre el servicio en el manifiesto:

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

Paso 3: Invoque el servicio desde la Actividad, pasando un objeto PendingResult que el Servicio usará 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: maneje 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
fuente
IntentService es la forma correcta de hacer esto, no desarraigar porque AsyncTask es exactamente la forma de no hacerlo.
Brill Pappin
3
@BrillPappin Estoy casi totalmente de acuerdo y he vuelto a redactar para enfatizar los inconvenientes de AsyncTask. (Todavía creo que hay un número muy pequeño de casos en los que, si realmente sabe lo que está haciendo, podría estar bien usar AsyncTask, pero la respuesta aceptada no señala ningún inconveniente y es demasiado popular para el bien de Android).
Stevie
1
¿Realmente necesitas IllustrativeRSS? ¿Qué pasa si no estás trabajando con material RSS?
Cullub
En mi opinión, Google debería cambiar su mala implementación de la recolección de basura en lugar de poner el berdon del lado de los programadores. Esta es la responsabilidad del sistema operativo. Si un programador usa IntentService o Service para hacer el trabajo debido al problema fundamental en la implementación de Android, después de algunos años Google dirá que IntentService también es una mala práctica y sugiere algo más. Y esta historia continúa ... Entonces, los desarrolladores de Google deberían resolver la mala gestión de la memoria de Android, no los programadores.
saeed khalafinejad
Solo voy a decirlo: esta respuesta parece más una forma de compartir el proyecto que una mejor alternativa AsyncTask. El error estaba destinado a evitar que los desarrolladores se retrasen en la interfaz de usuario, no necesariamente para atraerlos a arriesgar la seguridad con un montón de intentos / servicios.
Carro abandonado el
144

No puede realizar E / S de red en el subproceso de interfaz de usuario en Honeycomb . 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 hacer que el sistema operativo mate su aplicación por mal comportamiento. Deberá ejecutar un proceso en segundo plano o utilizar AsyncTask para realizar su transacción de red en un subproceso en segundo plano.

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

Mark Allison
fuente
76
  1. No utilice el modo estricto (solo en modo de depuración)
  2. No cambie la versión del SDK
  3. No use un hilo separado

Usar servicio o AsyncTask

Consulte también la pregunta de desbordamiento de pila:

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

venergiac
fuente
8
Quizás valga la pena enfatizar el punto de que si usa un Servicio, aún necesitará crear un subproceso separado: las devoluciones de llamadas de servicio se ejecutan en el subproceso principal. Un IntentService, por otro lado, ejecuta su método onHandleIntent en un hilo de fondo.
Stevie
¡no debe usar AsyncTask para operaciones de larga duración! Las pautas especifican 2 a 3 segundos máx.
Dage
76

Hacer 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 agregue esto a AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
henry4343
fuente
55
Pero, ¿cómo podemos saber cuándo termina el hilo en esto para que podamos llevar a cabo el siguiente conjunto de tareas en el hilo de la interfaz de usuario? AsyncTask proporciona la posibilidad de hacerlo. ¿Hay alguna manera de hacer lo mismo con hilos ejecutables?
Piyush Soni
3
Procesará su código paso a paso, por lo que al final del código, debe usar el controlador de nuevo al hilo de la interfaz de usuario
henry4343
1
Puede usar la tarea asincrónica o el servicio de intención, porque se ejecuta en el subproceso de trabajo.
Chetan Chaudhari
63

Deshabilita el modo estricto con 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 se recomienda : use la AsyncTaskinterfaz.

Código completo para ambos métodos

Sandeep
fuente
2
Sí, se produciría un error ANR. significa que la aplicación no responde en 5 segundos.
Muhammad Mubashir
12
Esta es una muy mala respuesta. No debe cambiar la política del hilo sino escribir un código mejor: ¡no realice operaciones de red en el hilo principal!
shkschneider
@Sandeep Usted y otros espectadores también deberían leer esto. stackoverflow.com/a/18335031/3470479
Prakhar1001
53

Las operaciones basadas en la red no se pueden ejecutar en el hilo principal. Debe ejecutar todas las tareas basadas en la red en un subproceso secundario o implementar AsyncTask.

Así es como ejecuta una tarea en un hilo secundario:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();
Dhruv Jindal
fuente
1
Anonymous Runnable NO es la mejor manera, ya que tiene una referencia implícita a la clase adjunta y evita que se edite por GC hasta que se complete el subproceso. Además, este subproceso se ejecutará con la misma prioridad que el subproceso principal / de EE. UU., ¡Compitiendo con métodos de ciclo de vida y velocidades de fotogramas de la interfaz de usuario!
Yousha Aleayoub
49

Pon tu código dentro:

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
    }
}
Vaishali Sutariya
fuente
El segundo será mejor que el primero para una API superior a 11
Rohit Goswami
46

Esto sucede en Android 3.0 y superior. Desde Android 3.0 y superior, han restringido el uso de las operaciones de red (funciones que acceden a Internet) para que no se ejecuten en el hilo principal / hilo de la interfaz de usuario (lo que se genera en los métodos de creación y reanudación de la actividad).

Esto es para alentar el uso de hilos separados para las operaciones de red. Consulte AsyncTask para obtener más detalles sobre cómo realizar actividades de red de la manera correcta.

raihan ahmed
fuente
46

Usar las anotaciones de Android es una opción. Le permitirá simplemente ejecutar cualquier método en un hilo de fondo:

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

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

Tenga en cuenta que, aunque proporciona beneficios de simplicidad y legibilidad, tiene sus desventajas.

Oleksiy
fuente
66
@Gavriel crea duplicados de todo lo que anotes, ya sea un método, actividad, fragmento, singleton, etc., por lo que hay el doble de código y lleva más tiempo compilarlo. También puede tener algunos problemas debido a errores en la biblioteca. La depuración y la búsqueda de errores serían más difíciles.
Oleksiy
43

El error se debe a la ejecución de operaciones de larga ejecución en el subproceso principal. Puede rectificar fácilmente el problema utilizando AsynTask o Thread . Puede retirar esta biblioteca AsyncHTTPClient para un mejor manejo.

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
    }
});
Ashwin S Ashok
fuente
42

No debe realizar ninguna tarea que requiera mucho tiempo en el subproceso principal (subproceso de interfaz de usuario), como cualquier operación de red, E / S de archivo u operaciones de base de datos SQLite. Entonces, para este tipo de operación, debe crear un subproceso de trabajo, pero el problema es que no puede realizar directamente ninguna operación relacionada con la interfaz de usuario desde su subproceso de trabajo. Para eso, tienes que usar Handlery pasar el Message.

Para simplificar todas estas cosas, Android proporciona varias maneras, como AsyncTask, AsyncTaskLoader, CursorLoadero IntentService. Por lo tanto, puede utilizar cualquiera de estos según sus requisitos.

Kapil Vats
fuente
40

La mejor respuesta de spektom funciona perfecta.

Si está escribiendo el en AsyncTasklínea y no se extiende como una clase, y además de esto, si es necesario obtener una respuesta AsyncTask, puede usar el get()método que se muestra a continuación.

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

(De su ejemplo).

sivag1
fuente
55
usar get()es una mala idea ... hace que AsyncTask vuelva a "sincronizarse"
Selvin
¿Hay una mejor manera diferente de salir? @Selvin
sivag1
2
Creo que podría informar al hilo principal sobre el resultado. Por ejemplo, envíe una transmisión al hilo principal incluyendo el resultado.
Chine Gary
32

Esto solo se lanza para aplicaciones dirigidas al Honeycomb SDK o superior. Las aplicaciones que se dirigen a versiones anteriores de SDK pueden hacer redes en sus principales hilos de bucle de eventos.

¡El error es la advertencia del SDK!

sidra de pera
fuente
28

Para mí fue esto:

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

¡El dispositivo en el que estaba probando mi aplicación era 4.1.2, que es SDK Versión 16!

Asegúrese de que la versión de destino sea la misma que su Biblioteca de destino de Android. Si no está seguro de cuál es su biblioteca de destino, haga clic con el botón derecho en su Proyecto -> Crear ruta -> Android , y debería ser la que esté marcada.

Además, como otros han mencionado, incluya los permisos correctos para acceder a Internet:

<uses-permission android:name="android.permission.INTERNET"/>
rharvey
fuente
11
Déjame explicarte lo que estás haciendo aquí: NetworkOnMainThreadExceptiones The Guardian que te dice: no dispares a tu propio pie ... tu solución es: volvamos al pasado cuando no había Guardian, ahora puedo disparar a mi pie libremente
Selvin
1
También adopté este enfoque y no tuve ningún problema. Guardian es demasiado quisquilloso a veces.
FractalBob
25

Use esto en su actividad

    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();
        }
    });
dhiraj kakran
fuente
23

Solo para explicar algo explícitamente:

El hilo principal es básicamente el hilo de la interfaz de usuario.

Por lo tanto, decir que no puede realizar operaciones de red en el subproceso principal significa que no puede realizar operaciones de red en el subproceso de interfaz de usuario, lo que significa que tampoco puede realizar operaciones de red en un *runOnUiThread(new Runnable() { ... }*bloque dentro de otro subproceso.

(Acabo de pasar un largo momento tratando de averiguar por qué estaba obteniendo ese error en otro lugar que no sea mi hilo principal. Por eso; este hilo ayudó; y espero que este comentario ayude a alguien más).

Novak
fuente
22

Esta excepción ocurre debido a cualquier tarea pesada realizada en el hilo principal si esa tarea lleva demasiado tiempo .

Para evitar esto, podemos manejarlo usando hilos o ejecutores

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

Ya hay muchas respuestas excelentes sobre esta pregunta, pero han surgido muchas bibliotecas excelentes desde que se publicaron esas respuestas. Esto pretende ser una especie de guía para novatos.

Cubriré varios casos de uso para realizar operaciones de red y una solución o dos para cada uno.

ReST sobre HTTP

Típicamente Json, puede ser XML u otra cosa

Acceso completo a la API

Supongamos que está escribiendo una aplicación que permite a los usuarios rastrear los precios de las acciones, las tasas de interés y los tipos de cambio actuales. Encuentra una API de Json que se parece a esto:

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 de Square

Esta es una excelente opción para una API con múltiples puntos finales y le permite declarar los puntos finales ReST en lugar de tener que codificarlos individualmente como con otras bibliotecas como ion o Volley. (sitio web: http://square.github.io/retrofit/ )

¿Cómo se usa con la API de finanzas?

build.gradle

Agregue estas líneas a su nivel de módulo buid.gradle:

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

Fragmento de fragmentos de finanzas

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

Si su API requiere que se envíe una clave API u otro encabezado como un token de usuario, etc., Retrofit lo hace fácil (consulte esta increíble respuesta para obtener más detalles: https://stackoverflow.com/a/42899766/1024412 ).

Acceso único a la API ReST

Supongamos que está creando una aplicación de "clima meteorológico" que busca la ubicación del GPS del usuario y comprueba la temperatura actual en esa área y les informa el estado de ánimo. Este tipo de aplicación no necesita declarar puntos finales de API; solo necesita poder acceder a un punto final API.

Ion

Esta es una gran biblioteca para este tipo de acceso.

Lea la excelente respuesta de msysmilu ( https://stackoverflow.com/a/28559884/1024412 )

Cargar imágenes a través de HTTP

Voleo

Volley también se puede usar para las API de ReST, pero debido a la configuración más complicada que se requiere, prefiero usar Retrofit from Square como anteriormente ( http://square.github.io/retrofit/ )

Supongamos que está creando una aplicación de red social y desea cargar imágenes de perfil de amigos.

build.gradle

Agregue esta línea a su nivel de módulo buid.gradle:

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

ImageFetch.java

Volley requiere más configuración que Retrofit. Tendrá que crear una clase como esta para configurar RequestQueue, ImageLoader y ImageCache, pero no está tan mal:

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

Agregue lo siguiente a su archivo xml de diseño para agregar una imagen:

<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

Agregue el siguiente código al método onCreate (Fragmento, Actividad) o al constructor (Diálogo):

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

Picasso

Otra excelente biblioteca de Square. Consulte el sitio para ver algunos ejemplos excelentes: http://square.github.io/picasso/

KG6ZVP
fuente
16

En palabras simples

NO HAGA TRABAJOS DE RED EN EL HILO UI

Por ejemplo, si realiza una solicitud HTTP, esa es una acción de red.

Solución:

  1. Tienes que crear un nuevo hilo
  2. O use la clase AsyncTask

Camino:

Pon todos tus trabajos adentro

  1. run() método de nuevo hilo
  2. O doInBackground() método de la clase AsyncTask.

Pero:

Cuando obtiene algo de la respuesta de red y desea mostrarlo en su vista (como mostrar el mensaje de respuesta en TextView), debe volver al hilo de la interfaz de usuario .

Si no lo haces, lo conseguirás ViewRootImpl$CalledFromWrongThreadException.

¿Cómo?

  1. Mientras usa AsyncTask, actualice la vista desde el onPostExecute()método
  2. O llame al runOnUiThread()método y actualice la vista dentro del run()método.
Nabin
fuente
12

Puede mover una parte de su código a otro subproceso para descargar main thready evitar ANR , NetworkOnMainThreadException , IllegalStateException (por ejemplo, no se puede acceder a la base de datos en el subproceso principal, ya que puede bloquear la interfaz de usuario durante un largo período de tiempo).

Hay algunos enfoques que debe elegir dependiendo de la situación.

Java Thread o Android HandlerThread

Los subprocesos de Java son de un solo uso y mueren después de ejecutar su método de ejecución.

HandlerThread es una clase útil para comenzar un nuevo hilo que tiene un looper.

AsyncTask

AsyncTask está diseñado para ser una clase auxiliar alrededor de Thread and Handler y no constituye un marco genérico de subprocesos. Las AsyncTasks deberían usarse idealmente para operaciones cortas (unos segundos como máximo). Si necesita mantener los subprocesos en ejecución durante largos períodos de tiempo, se recomienda encarecidamente que utilice las diversas API proporcionadas por el paquete java.util.concurrent, como Ejecutor , ThreadPoolExecutor y FutureTask .

Implementación del grupo de subprocesos ThreadPoolExecutor , ScheduledThreadPoolExecutor ...

Clase ThreadPoolExecutor que implementa ExecutorService que proporciona un control preciso sobre el grupo de subprocesos (por ejemplo, tamaño del grupo central, tamaño máximo del grupo, tiempo de mantenimiento, etc.)

ScheduledThreadPoolExecutor: una clase que extiende ThreadPoolExecutor. Puede programar tareas después de un retraso determinado o periódicamente.

FutureTask

FutureTask realiza un procesamiento asincrónico, sin embargo, si el resultado aún no está listo o el procesamiento no se ha completado, al llamar a get () se bloqueará el hilo

AsyncTaskLoaders

AsyncTaskLoaders ya que resuelven muchos problemas inherentes a AsyncTask

IntentService

Esta es la opción de facto para el procesamiento de larga duración en Android, un buen ejemplo sería cargar o descargar archivos grandes. La carga y descarga pueden continuar incluso si el usuario sale de la aplicación y ciertamente no desea bloquear al usuario para que no pueda usar la aplicación mientras se realizan estas tareas.

JobScheduler

Efectivamente, debe crear un Servicio y crear un trabajo utilizando JobInfo.Builder que especifique sus criterios para cuándo ejecutar el servicio.

RxJava

Biblioteca para componer programas asincrónicos y basados ​​en eventos mediante el uso de secuencias observables.

Corutinas (Kotlin)

La esencia principal de esto es que hace que el código asíncrono se parezca mucho a sincrónico

Lea más aquí , aquí , aquí , aquí

yoAlex5
fuente
Trabajó para mí ... He usado AsyncTask ampliamente, pero cuando una tarea se está ejecutando, otra esperará. Quiero resolver esto Ahora trabajando con executeonexecutor. Veamos cómo se comportará en dispositivos con poca memoria.
Suraj Shingade
Eche un vistazo al método: asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, params); ejecutar su tarea simultáneamente
yoAlex5
10

Aunque arriba hay un gran grupo de soluciones, nadie mencionó com.koushikdutta.ion: https://github.com/koush/ion

También es asíncrono y muy simple de usar:

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
    }
});
msysmilu
fuente
10

Ya se han explicado las soluciones nuevas Thready AsyncTask .

AsyncTaskidealmente debe usarse para operaciones cortas. Normal Threadno es preferible para Android.

Eche un vistazo a una solución alternativa usando HandlerThread y Handler

HandlerThread

Clase práctica para comenzar un nuevo hilo que tiene un looper. El looper se puede usar para crear clases de manejador. Tenga en cuenta que start()aún debe llamarse.

Manipulador:

Un controlador le permite enviar y procesar mensajes y objetos ejecutables asociados con MessageQueue de un hilo. Cada instancia de Handler está asociada con un solo hilo y la cola de mensajes de ese hilo. Cuando crea un nuevo controlador, está vinculado a la cola de hilos / mensajes del hilo que lo está creando; a partir de ese momento, entregará mensajes y ejecutables a esa cola de mensajes y los ejecutará a medida que salgan del mensaje. cola.

Solución:

  1. Crear HandlerThread

  2. llamar start()enHandlerThread

  3. Crear Handlerobteniendo LooperdeHanlerThread

  4. Incruste su código relacionado con la operación de red en el Runnableobjeto

  5. Enviar Runnabletarea aHandler

Fragmento de código de muestra, qué dirección 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);

Ventajas de usar este enfoque:

  1. Crear nuevas Thread/AsyncTaskpara cada operación de red es costoso. El Thread/AsyncTaskserá destruido y recreado para las próximas operaciones de la red. Pero con Handlery HandlerThreadenfoque, puede presentar muchas operaciones de red (como tareas ejecutables) a solo HandlerThreadutilizando Handler.
Ravindra babu
fuente
8

Hay otra forma muy conveniente de abordar este problema: utilizar las capacidades de concurrencia de rxJava. Puede ejecutar cualquier tarea en segundo plano y publicar resultados en el hilo principal de una manera muy conveniente, por lo que estos resultados se entregarán a la cadena de procesamiento.

El primer consejo de respuesta verificado es usar AsynTask. Sí, esta es una solución, pero es obsoleta hoy en día, porque existen nuevas herramientas.

String getUrl() {
    return "SomeUrl";
}

private Object makeCallParseResponse(String url) {
    return null;
    //
}

private void processResponse(Object o) {

}

El método getUrl proporciona la dirección URL y se ejecutará en el hilo principal.

makeCallParseResponse (..) - funciona realmente

processResponse (..) - manejará el resultado en el hilo principal.

El código para la ejecución asincrónica se verá así:

rx.Observable.defer(new Func0<rx.Observable<String>>() {
    @Override
    public rx.Observable<String> call() {
        return rx.Observable.just(getUrl());
    }
})
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.io())
    .map(new Func1<String, Object>() {
        @Override
        public Object call(final String s) {
            return makeCallParseResponse(s);
        }
    })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
             processResponse(o);
        }
    },
    new Action1<Throwable>() {
        @Override
        public void call(Throwable throwable) {
            // Process error here, it will be posted on
            // the main thread
        }
    });

En comparación con AsyncTask, este método permite cambiar de programador un número arbitrario de veces (por ejemplo, buscar datos en un programador y procesar esos datos en otro (por ejemplo, Scheduler.computation ()). También puede definir sus propios programadores.

Para usar esta biblioteca, incluya las siguientes líneas en su archivo build.gradle:

   compile 'io.reactivex:rxjava:1.1.5'
   compile 'io.reactivex:rxandroid:1.2.0'

La última dependencia incluye soporte para el planificador .mainThread ().

Hay un excelente libro electrónico para rx-java .

Alex Shutov
fuente
un enfoque bastante extenso sería programarlo por nuestra cuenta y no sé por qué deberíamos hacerlo si ya es una opción en el lado de Android.
Prakhar1001
8

RxAndroides otra mejor alternativa a este problema y nos salva de las molestias de crear hilos y luego publicar resultados en el hilo de la interfaz de usuario de Android. Solo necesitamos especificar hilos en los que se deben ejecutar las tareas y todo se maneja 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. Al especificar (Schedulers.io()), RxAndroid se ejecutará getFavoriteMusicShows() en un hilo diferente.

  2. Al usar AndroidSchedulers.mainThread()queremos observar este Observable en el hilo de la interfaz de usuario, es decir, queremos que nuestra onNext()devolución de llamada se invoque en el hilo de la interfaz de usuario

Shinoo Goyal
fuente
8

El subproceso principal es el subproceso de interfaz de usuario, y no puede realizar una operación en el subproceso principal que puede bloquear la interacción del usuario. Puedes resolver esto de dos maneras:

Fuerza para hacer la tarea en el hilo principal como este

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

O cree un controlador simple y actualice el hilo principal si lo desea.

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

Y para detener el uso del hilo:

newHandler.removeCallbacks(runnable);

Para obtener más información, consulte esto: Roscado sin dolor

Sharath kumar
fuente
Gracias. La versión 1 ayuda cuando se agrega como primera acción en onCreate.
Ingo
7

Esto funciona. Acabo de hacer la respuesta del Dr. Luji un poco más simple.

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

En Android, las operaciones de red no se pueden ejecutar en el hilo principal. Puede utilizar Thread, AsyncTask (tareas de ejecución corta), Servicio (tareas de ejecución prolongada) para realizar operaciones de red.

Ponsuyambu Velladurai
fuente