Platform.runLater y Tarea en JavaFX
He estado investigando un poco sobre esto pero todavía estoy MUY confundido, por decir lo menos.
¿Alguien puede darme un ejemplo concreto de cuándo usar Task
y cuándo usar Platform.runLater(Runnable);
? ¿Cuál es exactamente la diferencia? ¿Existe una regla de oro sobre cuándo utilizar alguno de estos?
Corríjame también si me equivoco, pero ¿no son estos dos "Objetos" una forma de crear otro hilo dentro del hilo principal en una GUI (utilizada para actualizar la GUI)?
Úselo Platform.runLater(...)
para operaciones rápidas y simples y Task
para operaciones complejas y grandes.
- Caso de uso para
Platform.runLater(...)
- Caso de uso para
Task
: Ejemplo de tarea en la aplicación Ensemble
Ejemplo: ¿Por qué no podemos usarlo Platform.runLater(...)
para cálculos largos? (Tomado de la referencia siguiente).
Problema: hilo en segundo plano que solo cuenta de 0 a 1 millón y actualiza la barra de progreso en la interfaz de usuario.
Código usando Platform.runLater(...)
:
final ProgressBar bar = new ProgressBar();
new Thread(new Runnable() {
@Override public void run() {
for (int i = 1; i <= 1000000; i++) {
final int counter = i;
Platform.runLater(new Runnable() {
@Override public void run() {
bar.setProgress(counter / 1000000.0);
}
});
}
}).start();
Este es un horrible trozo de código, un crimen contra la naturaleza (y la programación en general). Primero, perderás células cerebrales con solo mirar este doble anidamiento de Runnables. En segundo lugar, va a inundar la cola de eventos con pequeños Runnables; de hecho, un millón de ellos. Claramente, necesitábamos alguna API para facilitar la escritura de trabajadores en segundo plano que luego se comunican con la interfaz de usuario.
Código usando tarea:
Task task = new Task<Void>() {
@Override public Void call() {
static final int max = 1000000;
for (int i = 1; i <= max; i++) {
updateProgress(i, max);
}
return null;
}
};
ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
No sufre ninguno de los defectos exhibidos en el código anterior.
Referencia: subprocesamiento de trabajadores en JavaFX 2.0
Platform.runLater
: Si necesita actualizar un componente de la GUI desde un hilo que no es GUI, puede usarlo para poner su actualización en una cola y será manejada por el hilo de la GUI lo antes posible.Task
implementa laWorker
interfaz que se utiliza cuando necesita ejecutar una tarea larga fuera del subproceso de la GUI (para evitar congelar su aplicación) pero aún necesita interactuar con la GUI en algún momento.
Si estás familiarizado con Swing, el primero equivale al concepto de SwingUtilities.invokeLater
y el segundo al concepto de SwingWorker
.
El javadoc de Task ofrece muchos ejemplos que deberían aclarar cómo se pueden utilizar. También puede consultar el tutorial sobre concurrencia .
Ahora se puede cambiar a la versión lambda.
@Override
public void actionPerformed(ActionEvent e) {
Platform.runLater(() -> {
try {
//an event with a button maybe
System.out.println("button is clicked");
} catch (IOException | COSVisitorException ex) {
Exceptions.printStackTrace(ex);
}
});
}
Una razón para utilizar un Platform.runLater() explícito podría ser que vinculó una propiedad en la interfaz de usuario a una propiedad de servicio (resultado). Entonces, si actualiza la propiedad del servicio vinculado, debe hacerlo mediante runLater():
En el hilo de la interfaz de usuario, también conocido como hilo de la aplicación JavaFX:
...
listView.itemsProperty().bind(myListService.resultProperty());
...
en Implementación del servicio (trabajador en segundo plano):
...
Platform.runLater(() -> result.add("Element " + finalI));
...