Platform.runLater i Task w JavaFX

88

Zrobiłem trochę badań na ten temat, ale nadal jestem BARDZO zdezorientowany, delikatnie mówiąc.

Czy ktoś może podać mi konkretny przykład, kiedy używać, Taska kiedy używać Platform.runLater(Runnable);? Jaka dokładnie jest różnica? Czy istnieje złota zasada, kiedy należy używać któregokolwiek z nich?

Popraw mnie również, jeśli się mylę, ale czy te dwa „Obiekty” nie są sposobem na utworzenie innego wątku w głównym wątku w GUI (używanym do aktualizacji GUI)?

Marc Rasmussen
źródło

Odpowiedzi:

108

Używaj Platform.runLater(...)do szybkich i prostych operacji oraz Taskdo złożonych i dużych operacji.

Przykład: Dlaczego nie możemy używać Platform.runLater(...)do długich obliczeń (wzięte z poniższego odniesienia).

Problem: Wątek w tle, który liczy od 0 do 1 miliona i aktualizuje pasek postępu w interfejsie użytkownika.

Kod przy użyciu 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();

To ohydny kawałek kodu, zbrodnia przeciwko naturze (i ogólnie programowaniu). Po pierwsze, stracisz komórki mózgowe, patrząc na podwójne zagnieżdżenie Runnables. Po drugie, kolejka wydarzeń zostanie zalana małymi Runnables - w rzeczywistości milionem z nich. Oczywiście potrzebowaliśmy interfejsu API, aby ułatwić pisanie procesów roboczych w tle, które następnie komunikują się z interfejsem użytkownika.

Kod przy użyciu zadania:

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

nie ma żadnych wad ujawnionych w poprzednim kodzie

Odniesienie: wątki robocze w JavaFX 2.0

niezmienny
źródło
Przykład zadania w łączu aplikacji Ensemble nie będzie już działać.
Cugomastik,
To jest poprawny link, ale nie mogę go edytować, ponieważ edycja ma tylko 2 znaki.
Aerus
29
Stwierdzenie, że jedna dotyczy operacji „szybkich”, a druga operacji „złożonych”, jest nieco myląca. Główna różnica polega przecież na tym, że jeden pozwala uruchomić kod w wątku JFX GUI z innego miejsca, a drugi robi dokładnie odwrotnie - pozwala na uruchomienie kodu w wątku tła z wątku GUI (dodając w stanie komunikować się z wątkiem GUI w procesie).
Siergiej Tachenov
Chcę zapisać obraz każdej sceny - a to zajmie trochę czasu. Czy spowoduje to problemy podczas uruchamiania w osobnym wątku za pośrednictwem zadania? Zrozumiałem, że wszystkie prace związane z GUI muszą być wykonywane w wątku FxApplication.
StephenBoesch
@ Sergey-Tachenov Więc używasz runLater () z wątku Zadania, aby zaktualizować wątek GUI w przypadkach, gdy chcesz zrobić więcej niż tylko zaktualizować pojedynczą właściwość, taką jak postęp?
zwykły użytkownik
58
  • Platform.runLater: Jeśli chcesz zaktualizować komponent GUI z wątku innego niż GUI, możesz użyć tego do umieszczenia aktualizacji w kolejce i zostanie ona obsłużona przez wątek GUI tak szybko, jak to możliwe.
  • Taskimplementuje Workerinterfejs, który jest używany, gdy musisz uruchomić długie zadanie poza wątkiem GUI (aby uniknąć zamrożenia aplikacji), ale nadal potrzebujesz na pewnym etapie interakcji z GUI.

Jeśli znasz Swing, pierwsza z nich jest równoważna, SwingUtilities.invokeLatera druga z koncepcją SwingWorker.

Plik javadoc programu Task podaje wiele przykładów, które powinny wyjaśniać, w jaki sposób można ich używać. Możesz również zapoznać się z samouczkiem dotyczącym współbieżności .

asylias
źródło
Dziękuję Czy możesz podać mały przykład korzystania z platformy? Czy możesz go używać poza wątkiem gui lub? A przykłady zadań w dokumentach są naprawdę niejasne
Marc Rasmussen
Tak Platform.runLater może być używany poza wątkiem GUI - to jest jego główny cel. Ten samouczek dotyczący zadań może okazać się bardziej pouczający niż plik javadoc.
assylias
3
Używasz Platform.runLater w ten sposób:Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
assylias
w jaki sposób będę mógł wtedy korzystać z komponentów GUI =: S
Marc Rasmussen
1
@KevinMeredith Metoda wywołania zadania nie powinna być wywoływana w wątku FX - ale zadanie udostępnia metody pomostowe (updateProgress itp.), Które działają w wątku FX. Aby uzyskać więcej informacji, zobacz javadoc i samouczki.
assylias
12

Można go teraz zmienić na wersję 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
źródło
8
Jeśli obsługujesz zdarzenie GUI, jesteś w wątku GUI. Dlaczego miałbyś wtedy używać runLater ()?
TFuto
Chociaż masz rację, odpowiedzią Caglara była próba podkreślenia użycia wyrażenia lambda, niekoniecznie po to, by dać dobry przykład kodowania. UżywamPlatform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin
2

Jednym z powodów użycia jawnej Platform.runLater () może być powiązanie właściwości w interfejsie użytkownika z właściwością usługi (wyniku). Więc jeśli zaktualizujesz powiązaną właściwość usługi, musisz to zrobić za pomocą runLater ():

W wątku interfejsu użytkownika, znanym również jako wątek aplikacji JavaFX:

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

w implementacji usługi (praca w tle):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
Alexander Pietsch
źródło