java - example - fatal exception main android os networkonmainthreadexception




¿Cómo corrijo android.os.NetworkOnMainThreadException? (20)

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

Use Service o AsyncTask

Véase también la pregunta sobre el desbordamiento de pila:

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

Recibí un error mientras ejecutaba 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?


Deshabilitas 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 recomendable : use la interfaz AsyncTask .

Código completo para ambos métodos.


En palabras simples,

NO HAGA TRABAJOS DE RED EN LA UI HILO

Por ejemplo, si realiza una solicitud HTTP, 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 dentro

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

Pero:

Cuando obtiene algo de la respuesta de la 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, obtendrás ViewRootImpl$CalledFromWrongThreadException .

¿Cómo?

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

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

new RetrieveFeedTask().execute(urlToRssFeed);

No olvide agregar esto al archivo AndroidManifest.xml :

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


Esto sucede en Android 3.0 y superior. Desde Android 3.0 y superiores, han restringido el uso de operaciones de red (funciones que acceden a Internet) para que no se ejecuten en el hilo principal / subproceso 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 fomentar el uso de subprocesos separados para las operaciones de red. Consulte AsyncTask para obtener más detalles sobre cómo realizar las actividades de la red de la manera correcta.


La respuesta superior de spektom funciona perfecto.

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

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

(De su ejemplo).


La respuesta aceptada tiene algunas desventajas significativas. No es recomendable usar AsyncTask para redes a menos que realmente sepa lo que está haciendo. Algunas de las desventajas incluyen:

  • Las clases de AsyncTask creadas como no internas estáticas tienen una referencia implícita al objeto de Actividad adjunto, su contexto y toda la jerarquía de la vista creada por esa actividad. Esta referencia evita que la Actividad se recoja como basura hasta que finalice 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 en función de la plataforma en la que se ejecuta: antes del nivel 4 de API, las AsyncTasks se ejecutan en serie en un solo hilo de fondo; desde el nivel de API 4 hasta el nivel de API 10, AsyncTasks se ejecuta en un grupo de hasta 128 subprocesos; a partir del nivel de API 11, AsyncTask se ejecuta en serie en un solo hilo de fondo (a menos que use el método executeOnExecutor sobrecargado y suministre 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 inadvertidas de orden de ejecución.

Si desea evitar las 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. Usar una biblioteca que haga un buen trabajo de esto para usted: hay una buena comparación de librerías de redes en esta pregunta , o
  2. IntentService lugar, IntentService un Service o IntentService , quizás con un PendingIntent para devolver el resultado a través del método onActivityResult la Actividad.

Enfoque IntentService

Lados bajos

  • Más código y complejidad que AsyncTask , aunque no tanto como podría pensar.
  • Encolará las solicitudes y las ejecutará en un único hilo de fondo. Puede controlar esto fácilmente reemplazando IntentService con una implementación de Service equivalente, tal como esta .
  • Um, no puedo pensar en ningún otro en este momento en realidad

Lados superiores

  • Evita el problema de pérdida 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 método onActivityResult
  • Mejor plataforma que AsyncTask para crear y reutilizar código de red robusto. Ejemplo: si necesita realizar una carga importante, puede hacerlo desde AsyncTask en una Activity , pero si el usuario cambia de contexto fuera de la aplicación para tomar una llamada telefónica, el sistema puede matar la aplicación antes de que se complete la carga. Es menos probable que mate una aplicación con un Service activo.
  • Si usa su propia versión concurrente de IntentService (como la que he enlazado anteriormente), puede controlar el nivel de concurrencia a través del Executor .

Resumen de implementación

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

Paso 1: Crea un IntentService para realizar la descarga. Puede decirle qué descargar a través de Intent extra y pasarle un PendingIntent para que devuelva el resultado a 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);
        }
    }
}

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: 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 github que contiene un proyecto completo de Android-Studio / gradle en funcionamiento está disponible here .


No debe realizar tareas que requieran mucho tiempo en el subproceso principal (subproceso UI), como cualquier operación de red, E / S de archivos o operaciones de base de datos SQLite. Por lo tanto, 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 Handler y pasar el Message .

Para simplificar todas estas cosas, Android proporciona varias formas, como AsyncTask , AsyncTaskLoader , CursorLoader o IntentService . Así que puedes usar cualquiera de estos de acuerdo a tus requerimientos.


No puede realizar I/O red en el subproceso de la interfaz de usuario en Honeycomb . Técnicamente, es posible en versiones anteriores de Android, pero es una muy mala idea ya que hará que la aplicación deje de responder y puede hacer que el sistema operativo anule su aplicación por mal comportamiento. Deberá ejecutar un proceso en segundo plano o usar AsyncTask para realizar su transacción de red en un hilo en segundo plano.

Hay un artículo sobre Painless Threading en el sitio del desarrollador de Android que es una buena introducción a esto, y le proporcionará una respuesta mucho más profunda de lo que se puede proporcionar de manera realista aquí.


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

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

Usar Anotaciones de Android es una opción. Te 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.


Ya hay muchas respuestas excelentes sobre esta pregunta, pero han surgido muchas bibliotecas excelentes desde que se publicaron esas respuestas. Esto está pensado como 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

Digamos que está escribiendo una aplicación que permite a los usuarios realizar un seguimiento de los precios de las acciones, las tasas de interés y las tasas de cambio de moneda. Encuentras 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 la plaza

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?

construir.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

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

FinanzasApiBuilder

public class FinancesApiBuilder {
    public static FinancesApi build(String baseUrl){
        return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(FinancesApi.class);
    }
}

Finanzas Fragmento de fragmentos

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 de API u otro encabezado como un token de usuario, etc., Retrofit lo hace fácil (consulte esta increíble respuesta para obtener más información: https://.com/a/42899766/1024412 ).

Un solo acceso a la API de ReST

Digamos que está creando una aplicación de "clima del clima" que busca la ubicación de GPS de los usuarios, verifica la temperatura actual en esa área y les dice el estado de ánimo. Este tipo de aplicación no necesita declarar puntos finales de API; solo necesita poder acceder a un punto final de API.

Ion

Esta es una gran biblioteca para este tipo de acceso.

Por favor, lea la gran respuesta de msysmilu ( https://.com/a/28559884/1024412 )

Cargar imágenes a través de HTTP

Voleo

Volley también se puede usar para ReST API, pero debido a la configuración más complicada que se requiere, prefiero usar Retrofit from Square como se indica 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.

construir.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. Necesitará crear una clase como esta para configurar un RequestQueue, un ImageLoader y un 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 plaza. Consulte el sitio para ver algunos ejemplos excelentes: http://square.github.io/picasso/


Casi siempre debe ejecutar las operaciones de red en un hilo o como una tarea asíncrona.

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

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 irregular de Internet) dejará de responder y se bloqueará, el usuario percibe la lentitud y tiene que hacer una matanza forzosa, y se arriesga a que el administrador de actividades mate su aplicación y le diga 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


Aunque arriba hay un grupo de soluciones enorme, nadie menciona 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
    }
});

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


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

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

No está permitido implementar operaciones de red en el subproceso de la interfaz de usuario en Android. Tendrá que usar la clase AsyncTask para realizar operaciones relacionadas con la red como enviar solicitudes de API, descargar imágenes de una URL, etc. y usar los métodos de devolución de llamada de AsyncTask, puede obtener el resultado en el método onPostExecute y estará en el subproceso de la interfaz de usuario. puede llenar la interfaz de usuario con datos del servicio web o algo así.

Ejemplo: Supongamos que desea descargar una imagen desde una URL: https://www.samplewebsite.com/sampleimage.jpg

Solución utilizando AsyncTask: son respectivamente.

    public class MyDownloader extends AsyncTask<String,Void,Bitmap>
    {
        @Override
        protected void onPreExecute() {
            // Show progress dialog
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //Populate Ui
            super.onPostExecute(bitmap);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            // Open URL connection read bitmaps and return form here
            return result;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            // Show progress update
            super.onProgressUpdate(values);
        }


    }
}

Nota: No olvide agregar el permiso de Internet en el archivo de manifiesto de Android. Funcionará a la perfección. :)


Nuevos Thready AsynTask soluciones que ya se han explicado.

AsyncTaskidealmente debería ser utilizado para operaciones cortas. Normal Threadno es preferible para Android.

Eche un vistazo a una solución alternativa utilizando 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()todavía debe ser llamado.

Entrenador de animales:

Un controlador le permite enviar y procesar objetos Message y Runnable asociados con MessageQueue de un subproceso. 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 hebra / cola de mensajes de la hebra que lo está creando; a partir de ese momento, entregará mensajes y se podrá ejecutar en esa cola de mensajes y los ejecutará a medida que salen del mensaje. cola.

Solución:

  1. Crear HandlerThread

  2. Llamar start()alHandlerThread

  3. Crear Handlerobteniendo LooperdeHanlerThread

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

  5. Enviar Runnabletarea aHandler

Fragmento de código de muestra, cuya 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 utilizar este enfoque:

  1. Crear nuevos Thread/AsyncTaskpara cada operación de red es costoso. Se Thread/AsyncTaskdestruirán y se volverán a crear 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.






thread-exceptions