25 marzo 2009

Android: TimerTask y GUI

Voy a retomar después de meses el blog, a ver si consigo documentar cada problema que me encuentro mientras programo con Android.

Hoy me he topado con un problema típico cuando programas con GUI gráfica, y es que muchas veces necesitamos actualizar o interactuar con la interfaz gráfica desde un thread que no es el principal, en jerga de Android: cuando queremos modificar el GUI de una Actividad desde otro thread. Concretamente en este caso, necesito que una tarea periódica (thread) muestre un pop-up en la interfaz (actividad) informando que está buscando señal GPS.

Lo primero es saber como ejecutar un método periódicamente en Android. Para ello podemos utilizar la clase nativa de Java llamada TimerTask. Podemos configurar el delay (de la primera ejecución) y el periodo(de la siguientes ejecuciones). Si desde el método run de esta TimerTask intentamos modificar cualquier cosa relacionado con la interfaz gráfica que se ejecuta en el thread principal, veremos como la aplicación se queda congelada, o simplemente muestra un error indicando que sólo se puede acceder a la interfaz desde el thread principal (actividad).

A continuación os dejo el código de cómo configurar y crear una TimerTask (intentad obviar la línea en negrita, que posteriormente explicaremos):

private void launchTask()
{
  int delay = 1000; // delay for 1 seconds
  int period = 120000; // repeat every 2minutes

  mTimer = new Timer();
  mTimer.scheduleAtFixedRate(new TimerTask() {
    public void run() {
      handler_ProgressDiaglog.sendEmptyMessage(0);
    }
  }, delay, period);
}


La línea que aparece en negrita en el cuerpo del método run, es la clave para la solución de nuestro problema. Básicamente lo que implementa por debajo es un paso de mensajes entre threads y que Java nos lo abstrae con la clase Handler(). Para que a nuestro thread principal (que tiene acceso al GUI) le llegue el mensaje que genera la TimerTask, tenemos que crear un atributo privado de tipo Handler. A continuación os dejo el código del Handler:

private Handler handler_ProgressDiaglog = new Handler() {
  @Override
  public void handleMessage(Message msg) {

    // We can modify the GUI here.

  }
};

NOTA: Es posible que un mismo handler recoga más de 1 mensaje, por lo que no es necesario crear un handler por mensaje que queramos transmitir. En nuestro caso, no nos interesa un mensaje en concreto, nada más que nos avise cuando la TimerTask ejecute.

Espero que os siva de ayuda esta recetilla de cómo solucionar en Android un problema típico que se da cuando queremos acceder al GUI desde un thread, que no es el principal.

2 comentarios:

Unknown dijo...

Muchas gracias. Me encontraba con el mismo problema por lo que me ha sido muy útil!!

José luis dijo...

excelente muchas gracias, lo necesitaba, saludos