thread runlater hilos example concurrencia java multithreading javafx

hilos - runlater javafx



FinalizaciĆ³n de tareas JavaFX y enhebrado JavaFX (1)

Las reglas básicas para enhebrar en JavaFX (y perdónenme si ya comprende algo de esto, solo quiero estar completo) son:

  1. Todo lo que bloquea la ejecución (o tarda mucho tiempo en ejecutarse) debe ejecutarse en un hilo de fondo, no en el hilo de la aplicación FX.
  2. Todo lo que cambie el estado de un Node que sea parte del gráfico de escena se debe ejecutar en el subproceso de la aplicación FX.

Para ayudar a lograr esto, la API de JavaFX proporciona una clase de Task . Esto tiene un método call() que devuelve un valor; es Runnable por lo que se puede proporcionar como argumento para un constructor Thread , o se puede pasar a un Executor . También proporciona devoluciones de llamada útiles que se garantiza que se ejecutarán en el subproceso de la aplicación FX, como setOnSucceeded , setOnFailed y varios métodos de update...() que actualizan propiedades como el progress y el message en el subproceso de la aplicación FX.

Sin embargo, la clase Task está realmente diseñada para tareas únicas: piense en aplicaciones que necesitan recuperar datos de una base de datos, por ejemplo, lo que puede llevar tiempo. Estos ejecutan una acción específica y devuelven un resultado. Su caso es algo diferente, en el sentido de que su hilo se ejecuta continuamente.

En este caso, probablemente sea mejor usar un Thread simple y usar Platform.runLater(...) para actualizar la interfaz de usuario. Platform.runLater(...) toma un Runnable y ejecuta su método run() en el subproceso de la aplicación FX.

No me queda claro por qué su código se comporta de la manera que describe, pero suponiendo que el método llamado snake.updatePosition() causa un cambio en la interfaz de usuario, que debe ejecutarse en el subproceso de la aplicación FX. En cualquier caso, lo intentaría

taskThread = new Thread(new Runnable() { public void run() { while(!Thread.currentThread().isInterrupted()){ Platform.runLater(new Runnable() { @Override public void run() { snake.updatePosition(); } }); try{ Thread.sleep(1000); } catch(InterruptedException e){ break; } } } });

Si está utilizando Java 8, todo se ve mucho mejor con lambdas reemplazando las clases internas anónimas:

taskThread = new Thread( () -> { while (! Thread.currentThread().isInterrupted()) { Platform.runLater( snake::updatePosition ); try { Thread.sleep(1000); } catch (InterruptedException exc) { break ; } } });

Otra técnica en JavaFX para ejecutar algo periódicamente es (¿ab?) Utilizar una animación:

Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { snake.updatePosition(); } } )); timeline.setCycleCount(Animation.INDEFINITE); timeline.play();

o, en Java 8, el algo resbaladizo

Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), event -> snake.updatePosition())); timeline.setCycleCount(Animation.INDEFINITE); timeline.play();

Recién comencé a aprender sobre JavaFX hoy y estaba tratando de obtener más información al hacer un clon de Snake, pero estoy teniendo problemas con el enhebrado. Quería crear un hilo que actualizara la posición de la serpiente en la pantalla, pero no podría usar uno en un modo Runnable normal, porque estaba usando JavaFX desde ese hilo para actualizar las posiciones de los rectángulos dibujados en la pantalla (que aprendí no puede hacer, y en su lugar debe usar Tareas, Servicios, Platform.runLater, etc.?) Mi clase que estaba creando el hilo de extends extiende JavaFX.scene.layout.Pane, y estoy tratando de usar una tarea para actualizar la posición de la serpiente . Mi problema es: la tarea parece ejecutarse solo una o dos veces y se cierra, sin que yo dé ninguna interrupción.

Constructor para la clase que extiende el Panel (La clase Snake amplía el Grupo):

public GameFrame(){ this.setPrefSize(800, 600); Snake snake = new Snake(); this.getChildren().add(snake); taskThread = new Thread(new Task<Void>() { protected Void call() throws Exception { while(!Thread.currentThread().isInterrupted()){ snake.updatePosition(); try{ Thread.sleep(1000); } catch(InterruptedException e){ break; } } return null; } }); taskThread.start(); }

Siento que realmente no entiendo qué es lo mejor que puedo hacer aquí, y lo que estoy tratando de hacer puede ser hackish. ¿Hay alguna sugerencia sobre lo que debería hacer o cómo podría solucionarlo?