Algo que no hemos visto hasta el momento en nuestro Curso Android es la capacidad de tener hilos paralelos de procesamiento.

Inicialmente, los sistemas operativos sólo eran capaces de realizar una única tarea de forma simultánea, lo que bloqueaba el proceso, y hasta el ordenador, cuando éste tenía que realizar cálculos importantes o una operación de entrada/salida.

Más adelante, los sistemas fueron capaces de ejecutar varios procesos diferentes a la vez, de forma concurrente, lo que permitía optimizar el uso de la CPU, aprovechando los tiempos en que ésta estaba desocupada para ejecutar otras tareas.

Curso 5 Android

Android es un sistema multi-proceso, lo que quiere decir que es capaz de ejecutar varios procesos (tareas) de forma simultánea, y multi-flujo, que significa que puede ejecutar de forma simultánea dos o más porciones de código del mismo proceso.

Recordemos que los procesos se ejecutan en contextos separados, por lo que no comparten datos, mientras que los flujos o hilos de un mismo proceso se ejecutan en el mismo contexto, por lo que comparten el mismo espacio de memoria y pueden interferir entre ellos, lo que debe ser tenido en cuenta por el programador.

Android es un sistema fuertemente orientado al usuario, y a la interacción de éste con el sistema, que se hace efectiva por medio de clics, pulsaciones en la pantalla o en los botones, etc.

Cuando el usuario interacciona con una aplicación y ésta está ocupada en otros quehaceres, el sistema le asigna un tiempo de gracia para que finalice lo que esté haciendo y atienda al usuario; asumiendo el sistema que la aplicación ha dejado de funciona en caso que no lo haga, lo que provoca que el sistema la finalice automáticamente, lo que se conoce con el nombre de ANR y que, junto con el de sobras conocido FC, es el mensaje de error más habitual en Android.

El programador deberá tener en cuenta este hecho y no realizar en el hilo principal operaciones que puedan bloquear el proceso, tales como bucles con un alto número de iteraciones, operaciones de entrada/salida, accesos a internet, etc.

Curso 5 Android

Convertir una aplicación single-thread en multi-thread

Android distingue entre hilo principal de un proceso y todos sus hilos secundarios, permitiéndose únicamente al hilo principal acceder a la interfase gráfica.

Así, aunque podemos usar un hilo separado para calcular los diferentes elementos de una lista (ListView), deberemos usar el hilo principal para añadir los diferentes elementos a la vista y para redibujarla, si es el caso.

En realidad, convertir una aplicación que se ejecuta en un único hilo en multi-hilo es más sencillo de lo que parece.

La clase Thread

Android implementa los diferentes threads que se ejecutan en un determinado momento usando la superclase Thread, que define un procedimiento denominado run que contiene todo el código que ejecutará un hilo concreto.

Así, para iniciar un nuevo hilo, tan sólo es necesario instanciar una variable de tipo Thread, en la que habremos implementado convenientemente el procedimiento mencionado en el apartado anterior, y ejecutar la función start, tal como se muestra más abajo.


class MyThread extends Thread
{
public void run ()
{
...
}
}

Thread myThread = new MyThread();
myThread.start();

El programador debe tener en cuenta que tras la llamada al procedimiento start, el proceso principal (el que ha hecho la llamada) continuará su ejecución, que se realizará de forma paralela a la del nuevo hilo.

Asimismo, debe tener en cuenta que el sistema no le notificará cuando el hilo finalice (al acabar la función run), por lo que deberá ser el propio hilo el que envíe la notificación, si es el caso.

Curso 5 Android

La clase Handler

Existen diferentes maneras de enviar mensajes entre los diferentes hilos que implementan un proceso, siendo la más elegante, y comúnmente utilizada, la que se implementa usando instancias de la clase Handler.

Un Handler es un objeto de sistema capaz de procesar los diferentes mensajes que recibe, según el tipo de éstos y los parámetros adicionales que se reciban.

Es el propio sistema el que se encarga de enviar, de forma asíncrona, los mensajes al Handler adecuado, que lo procesa en el contexto del flujo que lo ha creado, lo que permite contextualización.

Recordemos que una llamada a un procedimiento es asíncrona cuando no tenemos garantías de que el código llamado se ejecute antes de que la llamada finalice.


class MyHandler extends Handler
{
public void processMessage (Message message)
{
switch message.what
{
...
}
}
}

Handler myHandler = new MyHandler();

Message message = new Message();
message.what = 1;
myHandler.sendMessage(message);

Cómo visualizar datos en una aplicación multi-flujo

Como hemos indicado, sólo el hilo principal puede acceder a la UI, por lo que será éste el único que podrá interactuar con los diferentes elementos que conformen las diferentes ventanas de nuestra aplicación.

Comunicación entre hilos de un mismo proceso

De hecho, es una buena práctica que el hilo principal se encargue solamente de procesar los diferentes eventos que genere el usuario y/o la propia aplicación, iniciando hilos nuevos para implementar las operaciones de entrada/salida y/o aquellas operaciones de proceso de datos que no sean triviales, teniendo en cuenta que el coste (en tiempo) de crear un nuevo hilo, aunque no es elevado, no es despreciable.

Para ello definiremos una instancia de la clase Handler en el hilo principal que se encargará de procesar (asíncronamente) los mensajes que le envían los diferentes hilos indicando que han finalizado sus tareas, procediendo a redibujar vistas, añadir o eliminar elementos gráficos, etc en el hilo principal.

La clase Runnable

En Java no existe la herencia múltiple, por lo que cuando extendemos una clase de tipo Thread perdemos la posibilidad de hacer que extienda otra, de ahí que los threads se usen sólo para implementar operaciones que empiezan y acaban, ya que cuando la función run finaliza también se acaba el objeto que la define.

Para evitar esto, se creó la clase Runnable, que no extiende una clase, sino que permite implementar métodos de acceso a la misma, lo que permite una mayor versatilidad.

Así, la clase Thread se ocupa habitualmente de implementar operaciones complejas en tareas, mientras que la clase Runnable implementa tareas completas. Es decisión del programador elegir cuándo utilizar una u otra.

La próxima semana os enseñaremos cómo capturar los eventos (de usuario y de sistema), algunos de los cuales ya hemos introducido brevemente en sesiones anteriores.

Mientras tanto, puedes descargar la versión número 3 de la aplicación que estamos desarrollando, en la que comprobarás cómo obtenemos la lista de archivos de un directorio en un hilo separado y cómo enviamos un mensaje al hilo principal cuando esta lista ya está completa, procediendo éste a actualizar la lista que se muestra al usuario.

A vuestra disposición está el apartado oficial del Curso Android de Foro Móviles por si tenéis dudas generales.




Ingeniero en Informática por la Universidad Politécnica de Cataluña.Programador en perl, php, c++, Java,...Desarrollador reconocido del foro XDA-Developers.Usuario Android desde 2009, y actualmente también desarrollador de utilidades y ROMs para ese sistema.Mi primer smartphone fue un Qtek-S100, en 2005, y desde entonces ando buscando el dispositivo perfecto. Si no fuera por la crisis cambiaría de móvil varias veces al día :)

Sin comentarios

Deja una respuesta