10 de septiembre de 2009

SwingWorker

¿Qué está haciendo el programa ahora?

Un dibujo de un reloj de arena no suele responder la pregunta.

Para informar lo que va pasando, y actualizar una interfaz en java (Swing)
mientras algún hilo más interesante se ejecuta en background
existe el SwingWorker.

Típicos ejemplos son mover una barra, dar mensajes actualizados
de qué está haciendo el soft, informar progresos de algún proceso de fondo.

Hay un thread, llamado Event Dispatching Thread (EDT)
que es el encargado de actualizar los componentes visuales.
Este señor EDT sin embargo, no se ejecuta completamente si hay un thread corriendo,
es decir, no se actualizará la interfaz hasta que el hilo termine o ceda el control.

¿Cómo se usa el SwingWorker?

Se crea un objeto "worker" donde se implementa el hilo que cumpla la función,
o el "trabajo" que realizará en background del cual queremos tener noticias,
el hilo del worker no se ejecuta en la EDT, es decir, no actualiza directamente
la interfaz, sin embargo permite agregar propiedades que llaman a listener que
sí se van a ejecutar en EDT!

Básicamente como en este ejemplo

//Ejemplo. Crear objeto SwingWorker, implementar ahí lo interesa actualizar
SwingWorker worker = new SwingWorker() {
//Hay una propiedad "progress" (de tipo int),
//actualizable con setProgress(int p) que se hereda de Swingworker
//suficiente como para disparar un listener al ser modificada
//opcionalmente se le pueden agregar otras vamos a agregar una tipo String.
private String noticias; //propiedad agregada
//Método donde se informan los avances del trabajo
public void informarNoticias(String noticias_in) {
//se actualiza la propiedad y se dispara al listener
String old = this.noticias;
this.noticias = noticias_in;
//Cuando se modifica la propiedad se genera un evento
firePropertyChange("noticias", old, noticias_in);
}
@Override
public String doInBackground() throws InterruptedException {
//AQUI! el trabajo que va progresando del cual queremos tener noticias
informarNoticias("Comienzo a hacer la tarea");
//trabajo
informarNoticias("Va por la parte en la que...");
//...
//trabajo
//..
informarNoticias("Ya casi termina!");
//...
informarNoticias("Listo!");
return ("ok");
}
@Override
public void done() {
//Se ejecuta al finalizar doInBackground "cuando termina el trabajo"
//Lo hace en el EDT!! i.e. si aquí actualizamos una barra o un label
//los cambios se van a reflejar..
}
};
//Este código permite ser disparado ante cambios en las propiedades
//o dispararlo intencionalmente (forzando esos cambios)
//También se ejecuta en EDT!, es mucho más interesante que el "done()"
//porque se puede llamar desde cualquier lugar del doInBackground
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
//Se detecta cual es la propiedad que cambió
if ("noticias".equals(evt.getPropertyName())) {
//por ejemplo aqui se puede actualizar una barra, un textarea con un log, etc..
//jTextArea1.append("\n" + evt.getNewValue().toString());
//jTextArea1.setText(jTextArea1.getText());
}
}
}
);
//Finalmente una vez que todo está creado, implementado, ejecutamos el worker!
worker.execute();