web-dev-qa-db-fra.com

Tâche d'arrière-plan périodique JavaFX

J'essaie d'exécuter périodiquement dans le thread d'arrière-plan de l'application JavaFX, ce qui modifie certaines propriétés de l'interface graphique.

Je pense que je sais comment utiliser les classes Task et Service de javafx.concurrent Et je ne peux pas comprendre comment exécuter une telle tâche périodique sans utiliser Thread#sleep() méthode. Ce serait bien si je pouvais utiliser quelques Executor de Executors méthodes de fabrication (Executors.newSingleThreadScheduledExecutor())

J'ai essayé d'exécuter Runnable toutes les 5 secondes, ce qui redémarre javafx.concurrent.Service Mais il se bloque immédiatement lorsque service.restart Ou même service.getState() est appelé.

Donc finalement j'utilise Executors.newSingleThreadScheduledExecutor(), qui déclenche mon Runnable toutes les 5 secondes et que Runnable exécute un autre Runnable en utilisant:

Platform.runLater(new Runnable() {
 //here i can modify GUI properties
}

Cela a l'air très méchant :( Y a-t-il une meilleure façon de le faire en utilisant les classes Task ou Service?

54

Vous pouvez utiliser la chronologie pour ce qui importe:

Timeline fiveSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(5), new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent event) {
        System.out.println("this is called every 5 seconds on UI thread");
    }
}));
fiveSecondsWonder.setCycleCount(Timeline.INDEFINITE);
fiveSecondsWonder.play();

pour les processus d'arrière-plan (qui ne font rien à l'interface utilisateur), vous pouvez utiliser l'ancien bon Java.util.Timer:

new Timer().schedule(
    new TimerTask() {

        @Override
        public void run() {
            System.out.println("ping");
        }
    }, 0, 5000);
93
Sergey Grinev

Je préfère la pause de transition:

    PauseTransition wait = new PauseTransition(Duration.seconds(5));
    wait.setOnFinished((e) -> {
        /*YOUR METHOD*/
        wait.playFromStart();
    });
    wait.play();
11
Marcel

Voici une solution utilisant Java 8 et ReactFX . Dites que vous souhaitez recalculer périodiquement la valeur de Label.textProperty().

Label label = ...;

EventStreams.ticks(Duration.ofSeconds(5))          // emits periodic ticks
    .supplyCompletionStage(() -> getStatusAsync()) // starts a background task on each tick
    .await()                                       // emits task results, when ready
    .subscribe(label::setText);                    // performs label.setText() for each result

CompletionStage<String> getStatusAsync() {
    return CompletableFuture.supplyAsync(() -> getStatusFromNetwork());
}

String getStatusFromNetwork() {
    // ...
}

Par rapport à la solution de Sergey, vous ne dédiez pas l'intégralité du thread à l'obtention du statut du réseau, mais utilisez plutôt le pool de threads partagé pour cela.

6
Tomas Mikula

Vous pouvez également utiliser ScheduledService . J'utilise cette alternative après avoir remarqué que lors de l'utilisation de Timeline et PauseTransition, des interruptions de l'interface utilisateur se sont produites dans mon application, en particulier lorsque l'utilisateur interagit avec les éléments d'un MenuBar ( sur JavaFX 12). En utilisant le ScheduledService ces problèmes ne se sont plus produits.

class UpdateLabel extends ScheduledService<Void> {

   private Label label;

   public UpdateLabel(Label label){
      this.label = label;
   }

   @Override
   protected Task<Void> createTask(){
      return new Task<Void>(){
         @Override
         protected void call(){
           Platform.runLater(() -> {
              /* Modify you GUI properties... */
              label.setText(new Random().toString());
           });
           return null;
         }
      }
   }
}

Et puis, utilisez-le:

class WindowController implements Initializable {

   private @FXML Label randomNumber;

   @Override
   public void initialize(URL u, ResourceBundle res){
      var service = new UpdateLabel(randomNumber);
      service.setPeriod(Duration.seconds(2)); // The interval between executions.
      service.play()
   }
}
1
Renan