Platform.runLater und Task in JavaFX

87

Ich habe einige Nachforschungen angestellt, bin aber immer noch SEHR verwirrt, um es gelinde auszudrücken.

Kann mir jemand ein konkretes Beispiel geben, wann Taskund wann ich es verwenden soll Platform.runLater(Runnable);? Was genau ist der Unterschied? Gibt es eine goldene Regel, wann eine davon verwendet werden soll?

Korrigieren Sie mich auch, wenn ich falsch liege, aber sind diese beiden "Objekte" nicht eine Möglichkeit, einen weiteren Thread innerhalb des Hauptthreads in einer GUI zu erstellen (der zum Aktualisieren der GUI verwendet wird)?

Marc Rasmussen
quelle

Antworten:

107

Verwendung Platform.runLater(...)für schnelle und einfache Operationen sowie Taskfür komplexe und große Operationen.

Beispiel: Warum können wir nicht Platform.runLater(...)für lange Berechnungen verwenden (entnommen aus der folgenden Referenz).

Problem: Hintergrund-Thread, der nur von 0 bis 1 Million zählt und den Fortschrittsbalken in der Benutzeroberfläche aktualisiert.

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

Dies ist ein schrecklicher Teil des Codes, ein Verbrechen gegen die Natur (und die Programmierung im Allgemeinen). Erstens verlieren Sie Gehirnzellen, wenn Sie nur diese doppelte Verschachtelung von Runnables betrachten. Zweitens wird es die Ereigniswarteschlange mit kleinen Runnables überschwemmen - eine Million davon sogar. Natürlich brauchten wir eine API, um das Schreiben von Hintergrundarbeitern zu vereinfachen, die dann wieder mit der Benutzeroberfläche kommunizieren.

Code mit Aufgabe:

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

es leidet an keinem der Fehler, die im vorherigen Code gezeigt wurden

Referenz: Worker Threading in JavaFX 2.0

invariant
quelle
Aufgabenbeispiel im Ensemble App-Link funktioniert nicht mehr.
Cugomastik
Dies ist der richtige Link, aber ich kann ihn nicht bearbeiten, da die Bearbeitung nur aus 2 Zeichen besteht.
Aerus
29
Zu sagen, dass eine für "schnelle" Operationen und eine für "komplexe" Operationen etwas irreführend ist. Der Hauptunterschied besteht schließlich darin, dass man Code im JFX-GUI-Thread von einem anderen Ort aus ausführen kann und der andere genau das Gegenteil tut - ermöglicht das Ausführen von Code im Hintergrund-Thread vom GUI-Thread aus (was einen Bonus des Seins hinzufügt in der Lage, mit dem GUI-Thread im Prozess zu kommunizieren).
Sergei Tachenov
Ich möchte von jeder Szene ein Bild speichern - und das wird einige Zeit dauern. Wird dies Probleme verursachen, wenn es in einem separaten Thread über Task ausgeführt wird? Ich habe verstanden, dass alle GUI-bezogenen Arbeiten im FxApplication-Thread ausgeführt werden müssen.
Javadba
@ Sergey-Tachenov Sie verwenden also runLater () aus einem Task-Thread, um den GUI-Thread zu aktualisieren, wenn Sie mehr als nur eine einzelne Eigenschaft wie progress aktualisieren möchten?
Simpleuser
58
  • Platform.runLater: Wenn Sie eine GUI-Komponente von einem Nicht-GUI-Thread aktualisieren müssen, können Sie damit Ihr Update in eine Warteschlange stellen. Es wird so schnell wie möglich vom GUI-Thread verarbeitet.
  • TaskImplementiert die WorkerSchnittstelle, die verwendet wird, wenn Sie eine lange Aufgabe außerhalb des GUI-Threads ausführen müssen (um ein Einfrieren Ihrer Anwendung zu vermeiden), aber dennoch irgendwann mit der GUI interagieren müssen.

Wenn Sie mit Swing vertraut sind, entspricht SwingUtilities.invokeLaterdas erstere dem Konzept von und das letztere dem Konzept von SwingWorker.

Das Javadoc von Task enthält viele Beispiele, die verdeutlichen sollen, wie sie verwendet werden können. Sie können sich auch auf das Tutorial zur Parallelität beziehen .

Assylien
quelle
Vielen Dank Können Sie ein kleines Beispiel für die Verwendung der Plattform geben? Kannst du es außerhalb des GUI-Threads verwenden oder? Und die Aufgabenbeispiele in den Dokumenten sind wirklich unklar
Marc Rasmussen
Ja, Platform.runLater kann außerhalb des GUI-Threads verwendet werden - das ist der Hauptzweck. Dieses Tutorial zu Aufgaben ist möglicherweise informativer als das Javadoc.
Assylias
3
Sie verwenden Platform.runLater wie Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
folgt
Wie kann ich dann die GUI-Komponenten verwenden? =: S
Marc Rasmussen
1
@ KevinMeredith Die Aufrufmethode der Task sollte nicht im FX-Thread aufgerufen werden. Task bietet jedoch Bridge-Methoden (updateProgress usw.), die im FX-Thread ausgeführt werden. Weitere Informationen finden Sie in den Javadoc- und Tutorials.
Assylias
12

Es kann jetzt auf Lambda-Version geändert werden

@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
quelle
8
Wenn Sie ein GUI-Ereignis behandeln, befinden Sie sich im GUI-Thread. Warum sollten Sie dann runLater () verwenden?
TFuto
Während Sie Recht haben, versuchte Caglars Antwort, die Verwendung eines Lambda-Ausdrucks hervorzuheben, nicht unbedingt, um ein gutes Codierungsbeispiel zu geben. Ich benutzePlatform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin
2

Ein Grund für die Verwendung einer expliziten Platform.runLater () könnte sein, dass Sie eine Eigenschaft in der Benutzeroberfläche an eine Service- (Ergebnis-) Eigenschaft gebunden haben. Wenn Sie also die Eigenschaft des gebundenen Dienstes aktualisieren, müssen Sie dies über runLater () tun:

Im UI-Thread, der auch als JavaFX-Anwendungsthread bezeichnet wird:

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

in der Service-Implementierung (Hintergrundarbeiter):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
Alexander Pietsch
quelle