Usando efectívamente el AsyncTask Parte 2

Ver parte 1 aqui.

El otro tip del que les quería hablar con los AsyncTask es más que todo recalcar la importancia de implementar expícitamente la lógica dentro del método doInBackground que permita cancelar el AsyncTask.

Si recuerdan de la parte uno, el AsyncTask guarda una referencia “debil” de la actividad que lo ejecuta. Además, verifica que la Actividad este siendo ejecutada con normalidad, es decir, que la actividad no esté siendo terminada.

Con el código de la parte uno, si el usuario se sale de dicha actividad, vamos a tener el hilo de la actividad corriendo hasta que termine con lo que estaba haciendo. Esto puede  representar un problema ya que tenemos un AsyncTask ejecutandose sin necesidad. Esto es un gasto tanto en procesamiento, memoria, como en la posibilidad de ejecutar otras AsyncTask (recordemos que existe un límite).

La solución a esto es, si la actividad no ha terminado y ya no la ocupamos más, podamos llamar el método calcel(boolean). Esto provocará que el método isCancelled regrese “true”, además, para asegurar que onCancelled() sea llamado en vez de onPostExecute(Object), es importante checkear isCancelled dentro de doInBackground, asegurando que la tarea termine lo más pronto posible.

Por ejemplo:

public class MyActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Inicialización de la actividad, layout, etc
        MyLongTask task = new MyLongTask(this);
        task.execute("http://blog.fr4gus.com/api/test.json");
    }

    @Override
    protected void onPause() {
        // Persistamos cualquier cosa que ocupemos
    }
    
    static class MyLongTask extends AsyncTask<String, Void, Void> {
        WeakReference<MyActivity> context;

        public MyLongTask(MyActivity activity) {
            context = new WeakReference<MyActivity>(activity);
        }

        @Override
        protected void onPreExecute() {
            // Avísele al usuario que estamos trabajando
        }

        @Override
        protected Void doInBackground(String... params) {
            while(! isCancelled() ){
            // Aquí hacemos una tarea laaarga
            // Y ademas chequeamos que la tarea no ha sido cancelada

            }
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            MyActivity activity = context.get();
            if (activity != null && !activity.isFinishing()) {
                // Aquí actualizamos la UI con el resultado
            }
        }
    }
    
}

Los AsyncTask son una herramienta muy util, pero también nos pueden perjudicar si no sabemos utilizarlas correctamente. Pueden provocar leaks de memoria, o uso de recursos innecesariamente. El hecho de que para Android Honeycomb hayan regresado al esquema original con un solo hilo, indica que la idea es que hayan pocos hilos actualizando la interface. De todas maneras hay que aprovechar otras herramientas similares como el método runOnUiThread o los Handlers. La idea es minimizar los riesgos y aprovechar los recursos al máximo.