Подтвердить что ты не робот

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

Я занимаюсь некоторыми исследованиями по этому вопросу, но я все еще очень смущен, если не сказать больше.

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

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

4b9b3361

Ответ 1

Используйте 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

Ответ 2

  • Platform.runLater: если вам нужно обновить компонент GUI из потока, не являющегося GUI, вы можете использовать его, чтобы поместить свое обновление в очередь, и оно будет обработано потоком GUI как можно скорее.
  • Task реализует интерфейс Worker который используется, когда вам нужно запустить длинную задачу вне потока GUI (чтобы избежать зависания вашего приложения), но все же необходимо взаимодействовать с GUI на некотором этапе.

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

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

Ответ 3

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

@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);
        }
    });
}

Ответ 4

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

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

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

в реализации службы (рабочий стол):

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