Platform.runLater et Task dans JavaFX

88

J'ai fait des recherches à ce sujet mais je suis encore TRÈS confus pour dire le moins.

Quelqu'un peut-il me donner un exemple concret de quand utiliser Tasket quand utiliser Platform.runLater(Runnable);? Quelle est exactement la différence? Existe-t-il une règle d'or pour utiliser l'un de ces outils?

Corrigez-moi également si je me trompe, mais ces deux "objets" ne sont-ils pas un moyen de créer un autre thread à l'intérieur du thread principal dans une interface graphique (utilisée pour mettre à jour l'interface graphique)?

Marc Rasmussen
la source

Réponses:

108

À utiliser Platform.runLater(...)pour des opérations rapides et simples et Taskpour des opérations complexes et importantes.

Exemple: Pourquoi ne pouvons-nous pas utiliser Platform.runLater(...)pour de longs calculs (tirés de la référence ci-dessous).

Problème: thread d'arrière-plan qui ne compte que de 0 à 1 million et met à jour la barre de progression dans l'interface utilisateur.

Code utilisant 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();

C'est un morceau de code hideux, un crime contre la nature (et la programmation en général). Tout d'abord, vous perdrez des cellules cérébrales rien qu'en regardant cette double imbrication de Runnables. Deuxièmement, cela va submerger la file d'attente des événements avec de petits Runnables - un million d'entre eux en fait. De toute évidence, nous avions besoin d'une API pour faciliter l'écriture de travailleurs d'arrière-plan qui communiquent ensuite avec l'interface utilisateur.

Code utilisant la tâche:

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();

il ne souffre d'aucun des défauts présentés dans le code précédent

Référence: Worker Threading dans JavaFX 2.0

invariant
la source
Le lien Exemple de tâche dans l'application Ensemble ne fonctionnera plus.
Cugomastik
C'est le lien correct mais je ne peux pas le modifier car la modification ne comporte que 2 caractères.
Aerus
29
Dire que l'une pour les opérations «rapides» et l'autre pour les opérations «complexes» est un peu trompeuse. La principale différence, après tout, est que l'un permet d'exécuter du code dans le thread JFX GUI depuis un autre endroit, et l'autre fait exactement le contraire - permet d'exécuter du code dans le thread d'arrière-plan à partir du thread GUI (en ajoutant un bonus d'être capable de communiquer avec le thread GUI dans le processus).
Sergei Tachenov
Je veux enregistrer une image de chacune des scènes - et cela prendra du temps. Cela causera-t-il des problèmes lors de l'exécution sur un thread séparé via Task? J'ai compris que tout le travail lié à l'interface graphique doit avoir lieu sur le thread FxApplication.
StephenBoesch
@ Sergey-Tachenov Vous utilisez donc runLater () à partir d'un thread Task pour mettre à jour le thread GUI dans les cas où vous souhaitez faire plus que simplement mettre à jour une seule propriété comme progress?
simpleuser
58
  • Platform.runLater: Si vous avez besoin de mettre à jour un composant GUI à partir d'un thread non GUI, vous pouvez l'utiliser pour mettre votre mise à jour dans une file d'attente et elle sera gérée par le thread GUI dès que possible.
  • Taskimplémente l' Workerinterface qui est utilisée lorsque vous avez besoin d'exécuter une longue tâche en dehors du thread GUI (pour éviter de geler votre application) mais que vous avez encore besoin d'interagir avec l'interface graphique à un moment donné.

Si vous êtes familier avec Swing, le premier équivaut à SwingUtilities.invokeLateret le second au concept de SwingWorker.

Le javadoc de Task donne de nombreux exemples qui devraient clarifier comment ils peuvent être utilisés. Vous pouvez également vous référer au tutoriel sur la concurrence .

assylies
la source
Merci Pouvez-vous donner un petit exemple d'utilisation de la plateforme? Pouvez-vous l'utiliser en dehors du fil de l'interface graphique ou? Et les exemples de tâches dans la documentation ne sont vraiment pas clairs
Marc Rasmussen
Oui Platform.runLater peut être utilisé en dehors du thread GUI - c'est son objectif principal. Vous trouverez peut-être ce didacticiel sur les tâches plus informatif que le javadoc.
assylias
3
Vous utilisez Platform.runLater comme ceci:Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
assylias
comment vais-je pouvoir utiliser les composants de l'interface graphique alors =: S
Marc Rasmussen
1
@KevinMeredith La méthode d'appel de la tâche ne doit pas être appelée sur le thread FX - mais Task fournit des méthodes de pont (updateProgress etc.) qui s'exécutent sur le thread FX. Voir le javadoc et les didacticiels pour plus d'informations.
assylias
12

Il peut maintenant être changé en version 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);
        }
    });
}
Cugomastik
la source
8
Si vous gérez un événement GUI, vous êtes dans le thread GUI. Pourquoi utiliseriez-vous runLater () alors?
TFuto
Bien que vous ayez raison, la réponse de Caglar essayait de mettre en évidence l'utilisation d'une expression lambda, pas nécessairement pour donner un bon exemple de codage. J'utilisePlatform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin
2

Une raison d'utiliser une explicite Platform.runLater () pourrait être que vous avez lié une propriété dans l'interface utilisateur à une propriété de service (résultat). Donc, si vous mettez à jour la propriété du service lié, vous devez le faire via runLater ():

Dans le thread d'interface utilisateur également appelé thread d'application JavaFX:

...    
listView.itemsProperty().bind(myListService.resultProperty());
...

dans la mise en œuvre du service (travailleur en arrière-plan):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
Alexandre Pietsch
la source