R. Chavarria's Blog

Proud of developing software, proud of being an Engineer

Uso avanzado de SwingWorker

Este post pertenece a mi antiguo blog en wordpress, pero decidí pasarme a github:pages por las siguientes razones

En una entrada anterior vimos cómo hacer un uso básico de SwingWorker así como cuándo usarlo. Vimos que había dos métodos de recuperar el resultado de nuestros cálculos o tarea de larga duración.

Aquí veremos un uso avanzado de SwingWorker. Para ello realizaremos una sencilla aplicación que calcule los N primeros números de la serie de fibonacci, los vaya mostrando en un área de texto según los vaya calculando y mostraremos el progreso de la tarea un una barra de progreso.

La clase SwingWorker tiene la siguiente definición: SwingWorker<T, V>. En la entrada anterior vimos que la forma básica de recuperar el resultado de nuestros cálculos era a través del método get(), el cuál retorna un valor de tipo genérico T. El genérico V es el tipo de datos que admiten los métodos publish y process y es el tipo de datos en el que generaremos los resultados intermedios.

Para realizar nuestra tarea del cálculo de los números de fibonacci crearemos una clase FibonacciWorker que herede de SwingWorker y que haga uso de los métodos adecuados para notificar el progreso hecho en los cálculos. Esta notificación la haremos a través de eventos de cambios en una propiedad. Es posible utilizar PropertyChangelListeners con propiedades definidas por nosotros y utilizar el método firePropertyChange para lanzar estos eventos. Para este ejemplo utilizaremos la propiedad progress que es una propiedad que ya está enlazada por defecto. La propiedad state (indica el estado de la ejecución de la tarea: INCIADA, CANCELADA, COMPLETADA, …) también lo está.

Nuestra tarea hereda de SwingWorker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class FibonacciWorker extends SwingWorker<List<Integer>, Integer> {
  PrimeNumbersTask(JTextArea textArea, int numbersToCalculate) {
    //initialize
  }

  @Override
  public List<Integer> doInBackground() {
    while (numbers.size <= numberstoCalculate && !isCancelled()) {
      number = nextFibonacciNumber();
      numbers.add(number);
      publish(number);
      setProgress(100 * numbers.size() / numbersToCalculate);
    }
    return numbers;
  }

  @Override
  protected void process(List<Integer> chunks) {
    for (Integer number : chunks) {
      textArea.append(number + "\n");
    }
  }

  private Integer nextFibonacciNumber(){
    //  calculate next fibonacci number and return it
  }
}

En el código anterior se han omitido detalles de implementación para no desviar la atención. Del código anterior cabe destacar la llamada a publish en la línea 11, la llamada a setProgress (línea 12) y la implementación del método process.

Mediante la llamada al método publish indicamos a SwingWorker que vaya almacenando resultados intermedios con los cuales llamará al método process, el cual hemos sobrescrito y lo usaremos para mostrar esos resultados intermedios. Es muy importante recalcar que mientras el método publish se ejecuta en el hilo (thread) del cálculo exhaustivo, el método process se invoca en el Event Dispatching Thread (EDT), y es por ello por lo que es seguro actualizar el interfaz de usuario.

Con la llamada al método setProgress() estamos indicando que se lance un evento de cambio sobre la propiedad progress (propiedad creada por defecto junto con state para la notificación de cambios en propiedades). El evento propagado incluirá entre sus datos el parámetro que recibe la función. Para mostrar ese progreso utilizaremos una barra de progreso, la cual la registraremos como interesada en recibir eventos de cambios en las propiedades de FibonacciWorker.

Actualizar una barra de progreso
1
2
3
4
5
6
7
8
9
10
final JProgressBar progressBar = new JProgressBar(0, 100);
FibonacciWorker task = new FibonacciWorker(textArea, numbersToCalculate);
task.addPropertyChangeListener(
  new PropertyChangeListener() {
    public  void propertyChange(PropertyChangeEvent evt) {
      if ("progress".equals(evt.getPropertyName())) {
        progressBar.setValue((Integer)evt.getNewValue());
      }
    }
  });

En el siguiente enlace es posible ver una aplicación demostrando lo aprendido. Si se desea descargar el código fuente en forma de proyecto de NetBeans: FibonacciWorker NetBeans project.

http://rchavarria.wordpress.com/2010/01/08/swingworker-en-java-5/

Comments