Cómo usar subprocesos en Java Swing

La programación no es fácil, y agregar una interfaz de usuario en torno a la funcionalidad realmente puede dificultar la vida. Especialmente porque no todos los marcos de interfaz de usuario son seguros para subprocesos...

La programación no es fácil, y agregar una interfaz de usuario en torno a la funcionalidad realmente puede dificultar la vida. Especialmente porque no todos los marcos de interfaz de usuario son seguros para subprocesos (incluido Swing). Entonces, ¿cómo manejamos de manera eficiente la interfaz de usuario, ejecutamos el código de trabajo y comunicamos datos entre los dos, todo mientras mantenemos la interfaz de usuario receptiva?

Afortunadamente para los usuarios de Swing, hay algunas opciones, las cuales pueden hacer que la programación de GUI sea mucho más simple. Aquí hay dos de esas opciones.

Invocar más tarde

SwingUtilities.invokeLater() es excelente para actualizar la interfaz de usuario desde otro hilo. Tal vez tenga una tarea de ejecución prolongada y le gustaría actualizar una barra de progreso para proporcionar comentarios al usuario. Podrías intentar algo como esto:

startButton.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    progressBar.setValue(0);
    progressBar.setStringPainted(true);

    // Runs outside of the Swing UI thread
    new Thread(new Runnable() {
      public void run() {
        for (i = 0; i <= 100; i++) {

          // Runs inside of the Swing UI thread
          SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              progressBar.setValue(i);
            }
          });

          try {
            java.lang.Thread.sleep(100);
          }
          catch(Exception e) { }
        }
      }
    }).start();
  }
});

Con suerte, puede ver en este ejemplo cómo podría usar SwingUtilities.invokeLater() para comunicarse entre la interfaz de usuario y los subprocesos de trabajo. Puedes pensar en invokeLater como una simple devolución de llamada a la interfaz de usuario para enviar las actualizaciones que necesites.

Trabajador de columpio {#trabajador de columpio}

SwingWorker<T,V> se puede usar de manera similar a invokeLater, pero cada uno tiene sus puntos fuertes. Personalmente, prefiero usar SwingWorker para tareas de ejecución prolongada para las que no necesito actualizar la interfaz de usuario (como cargar un documento grande), mientras que invokeLater podría usarse más para tareas de ejecución prolongada que sí necesitan para actualizar la interfaz de usuario. SwingWorker se puede usar de esta manera con:

private Document doc;

JButton button = new JButton("Open XML");
button.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {

   // All code inside SwingWorker runs on a seperate thread
   SwingWorker<Document, Void> worker = new SwingWorker<Document, Void>() {
     @Override
     public Document doInBackground() {
       Document intDoc = loadXML();
       return intDoc;
     }
     @Override
     public void done() {
       try {
         doc = get();
       } catch (InterruptedException ex) {
         ex.printStackTrace();
       } catch (ExecutionException ex) {
         ex.printStackTrace();
       }
     }
   };

   // Call the SwingWorker from within the Swing thread
   worker.execute();
 }
});

Esta clase desglosa los eventos de los trabajadores en métodos que se pueden implementar según sus necesidades. Para un uso más avanzado, consulte los métodos publish(V... chunks) y process(List<V> chunks).

Recursos