2010-11-02 23 views
32

Tengo un problema con la clase AsyncTask. Parece que mi tarea deja de funcionar después de crear 4 o 5 tareas.AsyncTask doInBackground no se ejecuta

Tengo 2 actividades. MainActivity que solo contiene un botón que inicia una segunda actividad llamada ImageActivity.

ImageActivity es muy simple. tiene un onCreate que establece el diseño, y luego inicia una nueva AsyncTask que carga una imagen de internet. Esto funciona bien las primeras veces. Pero de repente deja de funcionar. El método onPreExecute se ejecuta cada vez, pero no el método doInBackground. Intenté simplificar el doInBackground con un bucle para dormir, y ocurre lo mismo. No puedo entender este comportamiento ya que asynctask se cancela y se establece en nulo en el método onDestroy. Así que cada vez que comienzo una nueva ImageActivity, también creo una nueva AsyncTask.

Recreo ImageActivity y la tarea presionando el botón Atrás, y luego haciendo clic en el botón en la actividad principal.

¿Alguna idea a alguien? Realmente estoy luchando con este.

UPDATE: Código que inicia el ImageActivity (dentro de un botón OnClickListener)

Intent intent = new Intent(); 
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); 
intent.setClassName(this, ImageActivity.class.getName()); 
startActivity(intent); 

El código anterior comienza esta actividad

public class ImageActivity extends Activity { 

    private AsyncTask<Void, Void, Void> task; 

    public void onCreate(Bundle bundle) { 
     super.onCreate(bundle); 
     setContentView(R.layout.main); 

     task = new AsyncTask<Void, Void, Void>() { 

      @Override 
      protected void onPreExecute() 
      { 
       Log.d(TAG, "onPreExecute()"); 
      } 

      @Override 
      protected Void doInBackground(Void... params) 
      { 
       Log.d(TAG, "doInBackground() -- Here is the download"); 
       // downloadBitmap("http://mydomain.com/image.jpg") 
       return null; 
      } 

      @Override 
      protected void onPostExecute(Void res) 
      { 
       Log.d(TAG, "onPostExecute()"); 
       if(isCancelled()){ 
        return; 
       } 
      } 
     }.execute(); 
    } 

    @Override 
    protected void onDestroy() 
    { 
     super.onDestroy(); 
     task.cancel(true); 
    } 
} 

UPDATE:

He probado utilizando una combinación de los hilos tradicionales y el método runOnUiThread, y parece funcionar mejor. Ahora el hilo se ejecuta todo el tiempo.

+0

Publica tu código. ¿Cómo lo llamas? No tenemos idea de cómo ayudarte sin el código – Falmarri

+0

Teniendo exactamente el mismo problema, después de iniciar varias AsyncTasks en mi aplicación, la aplicación solo se ejecuta hasta onPreExecute(), pero no ingresa doInBackground(). Probando el enfoque de Thread now ... –

+1

Estoy teniendo el mismo problema. Lo extraño es que varía de un dispositivo a otro. Ocurre en una tableta (Android 4.0), pero un teléfono (Android 2.3.4) que ejecuta exactamente el mismo código no lo tiene. – Hong

Respuesta

22

Extracción de la AsyncTask y usando un hilo tradicional en lugar de combinarlo con runOnUiThread parece funcionar. Pero todavía no he encontrado la razón por la cual AsyncTask es tan "inestable".

Este es el código que funciona para mí:

public class ImageActivity extends Activity { 

    private Thread worker; 

    public void onCreate(Bundle bundle) { 
     super.onCreate(bundle); 
     setContentView(R.layout.main); 

     worker = new Thread(new Runnable(){ 

      private void updateUI(final List<Object> list) 
      { 
       if(worker.isInterrupted()){ 
        return; 
       } 
       runOnUiThread(new Runnable(){ 

        @Override 
        public void run() 
        { 
         // Update view and remove loading spinner etc... 
        } 
       }); 
      } 

      private List<Object> download() 
      { 
       // Simulate download 
       SystemClock.sleep(1000); 
       return new ArrayList<Object>(); 
      } 

      @Override 
      public void run() 
      { 
       Log.d(TAG, "Thread run()"); 
       updateUI(download()); 
      } 

     }); 
     worker.start(); } 

    @Override 
    protected void onDestroy() 
    { 
     super.onDestroy(); 
     worker.interrupt(); 
    } 
} 
+1

Gracias, tengo el mismo problema (lo publiqué aquí en SO un poco tiempo atrás y no resolvió el problema) una búsqueda en Google me trajo aquí :) – Ryan

+5

Esto arroja luz sobre por qué AsyncTask puede haberle fallado a usted (¡y a mí!): http://foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html – Archie1986

+0

Solo compartiendo un pensamiento, recientemente encontré algo como el comportamiento vudú de 'AsyncTask'. Sin embargo, lo supere después de cerrar un 'AsyncTask' ya en ejecución en segundo plano. Raro, pero funcionó para mí. – faizanjehangir

0

No debe tener que preocuparse por el hilo de limpieza en Android, ya que es administrado por el sistema.

También publique el método de descarga de imágenes. ¿Has intentado también no cancelar el hilo en el método onDestroy()? ¿Cómo devuelve la imagen a su hilo de interfaz de usuario?

+0

El método downloadBitmap es un poco largo, y no es el problema. Me da el mismo problema si pruebo Thread.sleep (5000) simulando una descarga lenta. Cuando uso Thread.sleep, puedo iniciar ImageActivity 3-4 veces y la tarea funciona. Después de eso, la tarea solo imprime onPreExecute y no el mensaje de registro "doInBackground", lo que significa que nunca se llama por algún motivo. Y sí, intenté no cancelar la tarea, pero no ayudé. –

+0

¿Hay más código que haya omitido? –

0

El problema que creo que es la tarea de descargar la imagen pesada. Incluso si cancela la tarea asíncrona, la descarga de la imagen continuará ejecutándose y la tarea asíncrona no finalizará hasta que se complete la descarga. Es posible que desee comprobar el método isCancelled() en AyncTask mientras se realiza la descarga y anule la descarga si la tarea se cancela.

Como referencia, aquí está la documentación sobre el método cancel(): Intenta cancelar la ejecución de esta tarea. Este intento fallará si la tarea ya se ha completado, ya se ha cancelado o no se puede cancelar por algún otro motivo. Si tiene éxito, y esta tarea no ha comenzado cuando se invoca cancelar, esta tarea nunca debería ejecutarse. Si la tarea ya ha comenzado, el parámetro mayInterruptIfRunning determina si el hilo que ejecuta esta tarea debe interrumpirse para intentar detener la tarea. Llamar a este método dará como resultado que se invoque a cancelado (objeto) en el hilo de la interfaz de usuario después de que doInBackground (objeto []) regrese. Llamar a este método garantiza que onPostExecute (Object) nunca se invoque. Después de invocar este método, debe verificar el valor devuelto por isCancelled() periódicamente desde doInBackground (Object []) para finalizar la tarea lo antes posible.

1

Utilice traceview para investigar u obtener un volcado de hilo. Supongo que uno de tus hilos AsyncTask está pendiente de descarga.

AsyncTask tiene un pequeño grupo de subprocesos, por lo que si una de sus tareas se bloquea, podría terminar bloqueando el grupo de subprocesos.

Aquí hay una prueba rápida que puede ejecutar: en 4.3, veo que tengo solo 5 subprocesos simultáneos que puedo ejecutar. Cuando sale un hilo, se inician otros hilos.

private void testAsyncTasks() { 

     for (int i = 1; i <= 10; i++) { 
       final int tid = i; 
       new AsyncTask<Integer, Void, Void>() { 
        protected void onPreExecute() { 
          Log.d("ASYNCTASK", "Pre execute for task : " + tid); 
        }; 

        @Override 
        protected Void doInBackground(Integer... args) { 
          int taskid = args[0]; 
          long started = SystemClock.elapsedRealtime(); 
          Log.d("ASYNCTASK", "Executing task: " + taskid + " at " + started); 
          for (int j = 1; j <= 20; j++) { 
           Log.d("ASYNCTASK", " task " + taskid + ", time=" + (SystemClock.elapsedRealtime() - started)); 
           SystemClock.sleep(1000); 
          } 
          return null; 
        } 

        protected void onPostExecute(Void result) { 
          Log.d("ASYNCTASK", "Post execute for task : " + tid); 
        }; 
       }.execute(i); 

     } 
    } 
7

Me encontré con un problema similar. No puede tener varias Asynctasks ejecutándose en paralelo hasta SDK 11. Compruebe here for more info

0

Tuve esto también, no hay motivo real para no iniciar. Me di cuenta de que después de reiniciar el ADB funcionaba de nuevo. No estoy seguro de por qué esto es así, pero sí funcionó para mí

5

Acabo de toparme con este problema también. Si utiliza AsyncTask.execute, su tarea se ejecuta en una cola de serie (desde la fuente de Android 4.3):

Cuando introdujo por primera vez, AsyncTasks fueron ejecutados en serie en un solo hilo fondo. Comenzando con DONUT, esto se cambió a un grupo de subprocesos , lo que permite que varias tareas funcionen en paralelo. Comenzando con HONEYCOMB, las tareas se ejecutan en un único subproceso para evitar los errores de aplicación comunes causados ​​por la ejecución en paralelo.

Esto es consistente con el comportamiento que vi. Tenía un AsyncTask apareció un cuadro de diálogo en doInBackground y se bloqueó hasta que se cerró el cuadro de diálogo. El cuadro de diálogo necesitaba su propio AsyncTask para completar. El método AsyncTask.doInBackground del diálogo nunca se ejecutó porque el AsyncTask original todavía estaba bloqueado.

La solución es ejecutar el segundo AsyncTask en un Executor por separado.

+0

doInBackground() no debería llamarme a métodos de IU (por ejemplo, para abrir un cuadro de diálogo como mencionas). –

+0

Mostré el diálogo en el hilo principal, pero fue activado por 'doInBackground'. –

+1

Esta es la respuesta correcta que debe ser aceptada. (Acabo de terminar mi propia investigación sobre el tema y llegué a la misma conclusión - Executor por separado) –

Cuestiones relacionadas