Ejecutar una tarea en un nuevo hilo con AsyncTask

En Android es muy frecuente lanzar nuevos hilos. Tendremos que hacerlo siempre que exista la posibilidad de que una tarea pueda bloquear el hilo del interfaz de usuario. Esto suela ocurrir en cálculos complejos o en accesos a la red.

Tras ver el uso de las herramientas estándares en Java para crear hilos; en este apartado veremos una clase creada en Android que nos ayudará a resolver este tipo de problemas de forma más sencilla, la clase AsyncTask.

Una tarea asincrónica (asinc tasck) se define por un cálculo que se ejecuta en un hilo secundario y cuyo resultado queremos que se publique en el hilo del interfaz de usuario. Para crear una nueva tarea asíncrona puedes basarte en el siguiente esquema:

class MiTarea extends AsyncTask {

      

       @Override protected void onPreExecute() {

              …     

       }

       @Override protected Resultado doInBackground(Parametros... par) {

             …     

       }

  @Override protected void onProgressUpdate(Progreso... prog) {

       …     

 }

  @Override protected void onPostExecute(Resultado result) {

          …    

}

}


donde Parametros, Progreso y Resultado han de ser reemplazados por nombres de clases segun los tipos de datos con los que trabaje la tarea.

Los cuatro métodos que podemos sobreescribir corresponden a los cuatro pasos que seguirá AsyncTask para ejecutar la tarea:

  • onPreExecute(): En este método tenemos que realizar los trabajos previos a la ejcución de la tarea. Se utiliza normalmente para configurar la tarea y para mostrar en el la interfaz de usuario que empieza la tarea.
  • doInBackground(Parametros...): Es llamado cuando termina onPreExecute(). Es la parte más importante donde tenemos que realizar la tarea propiamente dicha. Es el único método de los cuatro que no se ejecuta en el hilo del interfaz de usuario. Lo va a hacer en un hilo nuevo creado para este propósito. Como hemos visto la clase AsyncTask ha de ser paramétrizada con tres tipos de datos. Es decir, cuando crees un AsyncTask la clase Parametros ha de ser reemplazada por una clase concreta que será utilizada para indicar la información de entrada a la tarea. Observa como en el parámetro de este método se han añadido tres puntos detrás de Parametros. Esto significa que se puede pasar al método un número variable de parámetros de esta clase[1].
  • onProgressUpdate(Progress...): Este método se utiliza para mostrar el progreso de la tarea al usuario. Se ejecuta en el hilo interfaz de usuario, por lo que podremos interactuar con las vistas. El progreso de una determinada tarea ha de ser controlado por el programador llamando al método publishProgress(Progress...) desde doInBackground(). La clase Progress es utilizada para pasar la información de progreso. Un uso frecuente es reemplazarla por Integer y representar el porcentaje de progreso en un valor entre el 0 y el 100.
  • onPostExecute(Result): Este método se usa para mostrar en el interfaz de usuario el resultado de la tarea. El parámetro de entrada (de la clase Result) corresponde con el objeto devuelto por el método doInBackground()

Una vez definida la clase descenciente de AsyncTask podremos arrancar una tarea de la siguiente forma:

MiTarea tarea = newMiTarea();

 tarea.execute(p1, p2, p3);

Donde p1, p2, p3 ha de ser una lista de objetos de la clase Parametros, pudiendo introducirse un número variable de parámetros. Ha y que resaltar que execute() es un método asíncrono. Esto significa que, tras llamardo, se pondrá en marcha la tarea en otro hilo, pero en paralelo se continuarán ejecutando las instrucciones que hayas escrito a continuación de execute. En nombre se AsyncTask se ha puesto precisamente por este comportamiento.

En el siguiente diagrama se muestra el orden de ejecución de estos métodos:

 

Ejercicio paso a paso: Crear un nuevo hilo con AsyncTask

En este ejercicio resolveremos la operación del ejercicio anterior pero ahora usando AsyncTask en lugar de Thread

  1. Abre el proyecto Hilos creado en el ejercicio anterior.
  2. Dentro de MainActivity introduce el siguiente código:
 class MiTarea extends AsyncTask {

             @Override

             protected Integer doInBackground(Integer... n) {

                    return factorial(n[0]);

             }

             @Override

             protected void onPostExecute(Integer res) {

                    salida.append(res + "\n");

             }

       }

Cuando usamos esta clase hemos de comenzar decidiendo los tres tipos de datos que utilizaremos usando el siguiente esquema AsyncTask<Parametros, Progreso, Resultado>. En nuestra tarea necesitamos un entero como entrada, no usaremos información de progreso y devolveremos un entero. Estos tres tipos de datos solo pueden ser clases. Si queremos utilizar un tipo simple, como int, tendremos que usar una clase envolvente, como Integer[1].

En el método doInBackground() se ha indicado como parámetro Integer..., de manera que se le podrán pasar una lista variable de enteros. Aunque, en nuestro caso solo nos interesa el primero (n[0]), estamos obligados a sobreescribir el método exactamente como se espera, y no podemos quitar los .... En este método nos limitamos a calcular el factorial y devolverlo. Al terminar este método será llamado onPostExecute(), pasándole como parámetro el valor devuelto. Para terminar la explicación, recuerda que el método doInBackground() se ejecutará en un nuevo hilo, mientras que onPostExecute() se ejecutará en el hilo del interfaz de usuario.

  1. Comenta las dos últimas líneas del método y añade las siguientes:

            

MiTarea tarea = new MiTarea();

             tarea.execute(n);

La llamada a execute() provocará que los diferentes métodos definidos en MiTarea sean llamados en el orden adecuado.

  1. Comprueba que funciona correctamente.


[1] Ver Anexo A: Referencia Java - Envolventes (wrappers)

[2] Ver Anexo A: Referencia Java - métodos con argumentos variables en número