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
?
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);
Je préfère la pause de transition:
PauseTransition wait = new PauseTransition(Duration.seconds(5));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
wait.playFromStart();
});
wait.play();
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.
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()
}
}