El tutor de nuestro curso online de Desarrollo de Aplicaciones para Android, Víctor Ramírez Las,…
Multitarea en Android con clases AsyncTask, Thread, Handler y Runnable
2.
Multitarea en Android con clases AsyncTask, Thread, Handler y Runnable
En esta publicación vamos a conocer las principales características de las clases AsyncTask, Handler, Thread y Runnable que nos permiten implementar tareas en segundo plano en una Aplicación Android. Al final, encontrarás también un cuadro comparativo donde vemos las situaciones donde se recomienda el uso de AsyncTask o de Threads.
Como explicamos en la introducción a las tareas en background, cualquier componente de Android, como puede ser una Activity, se ejecuta en el hilo principal o main thread (UIThread).
Si bien es cierto, que para operaciones rápidas no se encontrará ningún problema, es probable que necesitemos realizar procesos más costosos, y por lo tanto el hilo principal, encargado de mostrar la interfaz de usuario, quedaría bloqueado, con la consiguiente lentitud de cara al usuario. Es aquí donde interviene el sistema operativo Android, monitorizando todos los procesos que están en ejecución, y forzando a salir de la aplicación a aquellos que superen los 5 segundos.
Para evitar este tipo de situaciones, Android proporciona una serie de clases, que permiten trabajar en segundo plano, para aquellas operaciones que necesiten un mayor tiempo para ser procesadas. Vamos a describirlas a continuación:
AsyncTask
Clase que permite comunicarse con el subproceso del hilo de interfaz de usuario o hilo principal. Para ello realiza operaciones en segundo plano, mostrando los resultados en subprocesos de la interfaz de usuario.
Características principales de la clase AsyncTask:
- Proporciona un mayor control y orden en la ejecución de nuestros procesos en segundo plano.
- Marco de trabajo para operaciones no muy costosas.
- Las tareas se ejecutan en un sólo hilo, para evitar errores derivados de ejecuciones en paralelo.
- Tarea asíncrona definida en 4 pasos: ( OnPreExecute(), doInBackground(Params...), OnProgressUpdate(Progress...) y onPostExecute(Result...)
- Se añadió a partir de la versión Honeycomb (API nivel 3, versión Android 3.0). Aquí, puedes consultar la documentación oficial de la clase AsyncTask
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } } |
Implementación de Asyntask
A nivel de implementación, se deberá heredar de la clase Asynctask, donde se definen tres tipos genéricos:
public class TareaAsyncTask extends AsyncTask<params, progress, result>{- Params: Tipo de parámetro que se recibirá como entrada para la tarea en el método doInBackground(Params).
- Progress: Parámetros para actualizar el hilo principal o UIThread.
- Result: Es el resultado devuelto por el procesamiento en segundo plano.
Además de los tipos genéricos comentados anteriormente, será necesario sobreescribir los siguientes métodos para procesar y devolver la información tratada en segundo plano:
- OnPreExecute(): Método llamado antes de iniciar el procesamiento en segundo plano.
- doInBackground(Params...): En este método se define el código que se ejecutará en segundo plano. Recibe como parámetros los declarados al llamar al método execute(Params).
- OnProgressUpdate(Progress...): Este método es llamado por publishProgress(), dentro de doInBackground(Params) (su uso es muy común para por ejemplo actualizar el porcentaje de un componente ProgressBar).
- onPostExecute(Result...): Este método es llamado tras finalizar doInBackground(Params). Recibe como parámetro el resultado devuelto por doInBackground(Params).
- OnCancelled(): Se ejecutará cuando se cancele la ejecución de la tarea antes de su finalización normal.
Thread
clase que proporciona su propia unidad concurrente de ejecución, y se puede definir como la unidad de procesamiento más pequeña que puede ser planificada por un sistema operativo. Una de sus principales características es permitir a una aplicación realizar varias tareas de manera simultánea. Define sus propios argumentos, variables y pila de llamada a métodos.
Formas de ejecutar un hilo:
- Heredando (extends) de la clase base Thread y creando una instancia de dicha clase.
- Implementando (implements) la interfaz Runnable, y creando una instancia de la clase que implementa dicha interfaz. Esta opción es muy útil cuando no es posible heredar de la clase base Thread.
Nota: Ambas formas deben realizar la llamada al método start(), para procesar el código situado en el método run().
Características principales de la clase Thread:
- Ejecución de tareas en paralelo o de manera concurrente.
- Los hilos de ejecución comparten el espacio de memoria.
- El conjunto de hilos en ejecución que comparten los mismos recursos es conocido como un proceso.
- Cualquier modificación de un dato en memoria, por parte de un hilo, permite el acceso al dato modificado para el resto de hilos de manera inmediata.
- Cada hilo presenta de manera propia el contador de programa, la pila de ejecución (o pila de llamadas, que consiste en una estructura dinámica de datos, en las que el último proceso en entrar será el primero en salir, o lo que es lo mismo, el último proceso se ejecutará primero) y el estado de la CPU.
- Un proceso seguirá en ejecución si al menos uno de sus hilos de ejecución sigue activo. Si un proceso finaliza, todos sus hilos de ejecución también lo harán.
- Añadida a partir de la API nivel 1, puedes encontrar aquí la documentación oficial de la clase Thread
Implementación de Thread
Se instancia la clase Thread, posteriormente se implementa el método run(), dónde se definirá el código a ejecutar cuando la instancia creada llame al método start().
1 2 3 4 5 6 7 8 9 10 |
Thread hilo1 = new Thread(new Runnable() { @Override public void run() { //Código a ejecutar } }); hilo1.start(); |
Handler
Es aquella que permite manejar y procesar mensajes, proporcionando un mecanismo para su envío (a modo de puente) entre threads o hilos, y así poder enviar mensajes desde nuestro hilo secundario al UIThread o hilo principal.
Características principales de la clase Handler:
- Permiten poner en cola una acción que se realiza en un subproceso distinto al suyo.
- Cada instancia de la clase Handler está asociada a un sólo hilo y a la cola de mensajes de este.
- Comunicación del hilo secundario con el hilo principal o UIThread a través de un controlador.
- Proporciona varios métodos para poner en cola un mensaje en la parte delantera o enviar al final de la cola después de un determinado tiempo ( sendMessageAtFrontOfQueue(Message msg) y sendMessageAtTime(Message msg, long uptimeMillis) )
- Añadida a partir de la API nivel 1, puedes consultar aquí la documentación oficial de la clase Handler
Implementación de Handler
Se instancia la clase Handler, para posteriormente llamar al método sendMessage(), encargado de avisar al UIThread para realizar las tareas de repintado incluidas en el método handleMessage(Message msg).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { } }; [...] handler.sendMessage(handler.obtainMessage()); [...] |
Runnable
Clase que representa un comando que puede ser ejecutado. Es muy común su uso para la ejecución de código en un hilo diferente.
Dicha clase implementa el método run()
, que será llamado cuando un hilo es iniciado y creado dentro de una clase que implemente la interfaz Runnable
.
Implementación de Runnable
Bastará con implementar la clase Runnable, y añadir el método run()
(tras implementar la clase Runnable, nos indicará un error, y sólo deberemos posicionarnos sobre él y pulsar las teclas Alt + Enter y seleccionar la opción «Implement Methods», seleccionando el método a implementar, en nuestro caso el método run()
):
1 2 3 4 5 6 7 8 9 |
public class Hilo implements Runnable { @Override public void run() { //Código a ejecutar al lanzar hilo. } } |
Aquí puedes consultar la documentación oficial de la clase Runnable
AsyncTask vs Thread
Para finalizar esta presentación de las clases que nos permiten implementar tareas en segundo plano en Android, vamos a comprender las situaciones en las que se recomienda el uso de la clase AsyncTask o la clase Thread, dependiendo de las necesidades del proyecto:
Uso de AsynTask | Uso de Threads |
|
|
En la próxima publicación veremos un proyecto ejemplo donde gestionaremos varios hilos en segundo plano mediante el uso de la clase AsyncTask.