Platform.runLater и задача в JavaFX

89

Я провел некоторые исследования по этому поводу, но я все еще ОЧЕНЬ смущен, если не сказать больше.

Может ли кто-нибудь дать мне конкретный пример, когда использовать, Taskа когда использовать Platform.runLater(Runnable);? В чем именно разница? Есть ли золотое правило, когда использовать что-либо из этого?

Также поправьте меня, если я ошибаюсь, но разве эти два «объекта» не являются способом создания другого потока внутри основного потока в графическом интерфейсе (используется для обновления графического интерфейса)?

Марк Расмуссен
источник

Ответы:

109

Используйте Platform.runLater(...)для быстрых и простых операций, а также Taskдля сложных и больших операций.

Пример: Почему нельзя использовать Platform.runLater(...)для долгих расчетов (взято из ссылки ниже).

Проблема: фоновый поток, который просто считает от 0 до 1 миллиона и обновляет индикатор выполнения в пользовательском интерфейсе.

Код с использованием 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();

Это отвратительный кусок кода, преступление против природы (и программирования в целом). Во-первых, вы потеряете клетки мозга, просто глядя на это двойное вложение Runnables. Во-вторых, очередь событий заполнится маленькими Runnables - на самом деле миллионом. Ясно, что нам нужен был некоторый API, чтобы упростить написание фоновых рабочих, которые затем взаимодействуют с пользовательским интерфейсом.

Код с использованием задачи:

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

он не страдает ни одним из недостатков, представленных в предыдущем коде

Ссылка: Worker Threading в JavaFX 2.0

инвариантный
источник
Ссылка на пример задачи в приложении Ensemble больше не работает.
Cugomastik 05
Это правильная ссылка, но я не могу ее редактировать, потому что редактируется всего 2 символа.
Aerus
29
Сказать, что один для «быстрых» операций, а другой - для «сложных», немного вводит в заблуждение. В конце концов, основное отличие состоит в том, что один позволяет запускать код в потоке графического интерфейса JFX из другого места, а другой делает прямо противоположное - позволяет запускать код в фоновом потоке из потока графического интерфейса (добавляя бонус в виде может взаимодействовать с потоком графического интерфейса пользователя в процессе).
Сергей Таченов 02
Я хочу сохранить изображение каждой сцены - и это займет время. Будет ли это вызывать проблемы при запуске в отдельном потоке через Task? Я понял, что вся работа, связанная с графическим интерфейсом, должна выполняться в потоке FxApplication.
StephenBoesch
@ Sergey-Tachenov Значит, вы используете runLater () из потока задач для обновления потока GUI в тех случаях, когда вы хотите сделать больше, чем просто обновить одно свойство, например, прогресс?
simpleuser
58
  • Platform.runLater: Если вам нужно обновить компонент графического интерфейса пользователя из потока, отличного от графического интерфейса, вы можете использовать это, чтобы поместить свое обновление в очередь, и оно будет обработано потоком графического интерфейса как можно скорее.
  • Taskреализует Workerинтерфейс, который используется, когда вам нужно запустить длинную задачу вне потока графического интерфейса пользователя (чтобы избежать зависания приложения), но на каком-то этапе все еще необходимо взаимодействовать с графическим интерфейсом.

Если вы знакомы с Swing, первый эквивалент, SwingUtilities.invokeLaterа второй - концепции SwingWorker.

В javadoc Task есть много примеров, которые должны прояснить, как их можно использовать. Вы также можете обратиться к руководству по параллелизму .

ассилий
источник
Спасибо. Можете ли вы привести небольшой пример использования платформы? Вы можете использовать его вне потока GUI или? И примеры задач в документации действительно неясны
Марк Расмуссен
Да Platform.runLater можно использовать вне потока графического интерфейса - это его основная цель. Вы можете найти этот учебник по задачам более информативным, чем javadoc.
assylias
3
Вы используете Platform.runLater вот так:Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
assylias
как я смогу использовать компоненты графического интерфейса тогда =: S
Марк Расмуссен
1
@KevinMeredith Метод вызова Задачи не должен вызываться в потоке FX, но Задача предоставляет методы моста (updateProgress и т. Д.), Которые выполняются в потоке FX. См. Javadoc и руководства для получения дополнительной информации.
assylias
12

Теперь его можно изменить на лямбда-версию

@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);
        }
    });
}
Цугомастик
источник
8
Если вы обрабатываете событие GUI, вы находитесь в потоке GUI. Зачем тогда использовать runLater ()?
TFuto
Хотя вы правы, ответ Каглара был попыткой выделить использование лямбда-выражения, не обязательно для того, чтобы дать хороший пример кодирования. ИспользуюPlatform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin
2

Одна из причин использования явного Platform.runLater () может заключаться в том, что вы связали свойство в пользовательском интерфейсе со свойством службы (результата). Поэтому, если вы обновляете свойство привязанной службы, вы должны сделать это через runLater ():

В потоке пользовательского интерфейса, также известном как поток приложения JavaFX:

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

в реализации Сервиса (фоновый работник):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
Александр Питч
источник