2012-08-28 18 views
27

Tengo 2 ASyncTasks, uno recupera un valor de un httpPost y el otro actualiza algunos elementos de la IU (incluyendo una vista de lista). El problema es que ambas ASyncTasks comparten el mismo hilo de fondo, si la operación de red comienza primero y se ejecuta lentamente (debido a una mala conectividad de red). El resto del hilo de fondo lleva demasiado tiempo haciendo que la aplicación sea irresponsable.ASyncTasks que bloquean a otros

Dado que ambos ASyncTasks son independientes, es bastante estúpido hacer esperar al otro. Sería más lógico que asynctasks diferentes clases utilizan diferentes hilos, ¿me equivoco?

Leyendo el ASyncTask doc. Habla sobre el uso de executeOnExecutor(), pero ¿cómo puedo resolverlo en un nivel de API inferior a 11?

Aquí va un pequeño ejemplo que reproduce el "problema"

 new Task1().execute(); 
     new Task2().execute(); 

Con

public class Task1 extends AsyncTask<Void, Void, Void> { 

    @Override 
    protected Void doInBackground(Void... params) { 
     GLog.e("doInBackground start 1"); 
     SystemClock.sleep(9000); 
     GLog.e("doInBackground end 1"); 
     return null; 
    } 

    @Override 
    protected void onPreExecute() { 
     GLog.e("onPreExecute 1"); 
     super.onPreExecute(); 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     GLog.e("onPostExecute 1"); 
     super.onPostExecute(result); 
    } 

} 

public class Task2 extends AsyncTask<Void, Void, Void> { 

    @Override 
    protected void onPreExecute() { 
     GLog.e("onPreExecute 2"); 
     super.onPreExecute(); 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 
     GLog.e("doInBackground start 2"); 
     SystemClock.sleep(9000); 
     GLog.e("doInBackground end 2"); 
     return null; 
    } 

    @Override 
    protected void onPostExecute(Void result) { 
     GLog.e("onPostExecute 2"); 
     super.onPostExecute(result); 
    } 

} 
+0

¿Por qué tiene una AsyncTask para actualizar la UI? Necesitas hacer eso sincrónicamente en el hilo Ii. ¿Tu UI AsyncTask hace algo más en el hilo de fondo? –

+0

Lo uso, por ejemplo, para mostrar una vista de lista con las noticias. En el comando onPreExecute muestro la interfaz de usuario loading(), en segundo plano recupero las noticias de Internet y en el onPostExecute asigno el adaptador a la vista de lista. ¿Es ese enfoque incorrecto? – Addev

+0

@Addev: ¿Por qué no acaba de iniciar el segundo 'AsyncTask' en el método' onPostExecute (...) 'del primero? Si lo que necesita es que una 'AsyncTask' se base en el resultado de la otra, entonces efectivamente tiene un requisito síncrono y no debe ejecutar dos operaciones asincrónicas en paralelo. – Squonk

Respuesta

46

Así es como me ocupe de esto en mi código:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
    new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
} else { 
    new MyAsyncTask().execute(); 
} 

y reemplazar MyAsyncTask con los suyos Task1 y Task2 respectivamente. Básicamente, el cambio en AsyncTask apareció en Honeycomb (consulte los documentos SDK de Android here en la sección "Orden de ejecución"), por lo que antes de iniciarlo como siempre, para HC y superior, use executeOnExecutor() si no le gusta el nuevo comportamiento (nadie lo hace) creo)

7

una forma un poco más general para hacer esto es poner dos métodos de ayuda en una clase de utilidad de este modo:

class Utils { 

    @SuppressLint("NewApi") 
    public static <P, T extends AsyncTask<P, ?, ?>> void execute(T task, P... params) { 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
      task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
     } else { 
      task.execute(params); 
     } 
    } 
} 

a continuación, puede ejecutar tareas con Utils.execute(mytask) o Utils.execute(mytask, params) y que va a hacerse cargo de ejecutándolos en paralelo.

+0

Gracias. Nota para la posteridad, no se moleste en intentar anular 'AsyncTask.execute()' - no puede; es 'final'. – TalkLittle

+0

Además, el primer método es innecesario y en realidad podría causar un comportamiento inesperado ('params' puede ser' null' en lugar de una matriz vacía). Quitarlo se compila/funciona bien en dispositivos pre-Honeycomb. – TalkLittle

+0

No es innecesario, guarda el tipeo :) –

0

El problema es que cada AsyncTask se ejecuta en el mismo ThreadPoolExecutor que está codificado en la API. Este ThreadPoolExecutor puede crear un número diferente de WorkerThread dependiendo de su versión de Android. No recuerdo las versiones numéricas, pero la idea es que en las versiones anteriores de Android era 1 WorkerThread. Luego se actualizó a 5 en versiones posteriores. Y recientemente se movió nuevamente a 1. Esta es la razón por la cual tus AsyncTasks están bloqueadas. Ejecutan todo en el mismo WorkerThread y, por lo tanto, se ejecutan secuencialmente. Intenta usar executeOnExecutor con THREAD_POOL_EXECUTOR para lograr una verdadera ejecución paralela. Sin embargo, puede usar esto solo desde la API 11.

Cuestiones relacionadas