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

Javafx, обновить ui из другого потока

У меня есть приложение javafx и рабочий поток, реализованный через javafx.concurrent.Task, который выполняет длительный процесс, который выполняет zipping и загрузку набора файлов.
Я подключил выполнение задачи к индикатору выполнения через progressProperty.
В дополнение к этому я хочу подробное состояние об обрабатываемом элементе, которое будет отправлено в ui. То есть имя обрабатываемого файла вместе с его размером и любой ошибкой, которая может возникнуть из процесса одиночного файла.
Обновление пользовательского интерфейса с помощью этой информации не может быть выполнено из рабочего потока, и я могу добавить его в синхронизированную коллекцию.
Но тогда мне нужно какое-то событие, чтобы сообщить пользовательскому интерфейсу, что новые данные доступны.
У javafx есть определенная поддержка этой проблемы?

обновление, лучшая формулировка

Вместо того, чтобы разрабатывать специальный механизм перекрестных потоков как Platform.runLater, я пытаюсь разрешить прослушивание каждого свойства из других потоков. Точно так же, как runningProperty и stateProperty, предоставляемые Task

4b9b3361

Ответ 1

У меня возникает аналогичная проблема, насколько я могу судить, вам приходится иметь дело с обработкой ошибок самостоятельно. Мое решение - обновить интерфейс через вызов метода:

Что-то вроде:

  try
  {
    //blah...
  }
  catch (Exception e)
  {
    reportAndLogException(e);
  }
  ...
  public void reportAndLogException(final Throwable t)
  {
    Platform.runLater(new Runnable() {
      @Override public void run() {
        //Update UI here     
      }
    });
  }

По сути, я просто перекладываю его обратно в поток пользовательского интерфейса для обновления (как и в большинстве других фреймворков).

Ответ 2

Этот ответ использует ту же концепцию, что и ответ Дэниела.

Enclosed - это копия примера частичного результата из Task javadoc (исправлено для синтаксических ошибок, которые в настоящее время встроены в Java 8 javadoc и для добавления более конкретных типов Generic). Вы можете использовать модификацию этого.

Поместите свои исключения из коллекции partialResults. Для вашего случая вам не нужно возвращать список исключений из задачи, но вместо этого можно разместить их в некоторых элементах управления пользовательским интерфейсом, который отображает исключения (например, ListView с отображением CellFactory для исключения). Обратите внимание, что сбор части partialResults не нужно синхронизировать, потому что он всегда обновляется и доступен в потоке пользовательского интерфейса JavaFX (обновление происходит с помощью вызова Platform.runLater(), аналогичного решению Daniel).

public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
    private ReadOnlyObjectWrapper<ObservableList<Rectangle>> partialResults =
            new ReadOnlyObjectWrapper<>(
                    this, 
                    "partialResults",
                    FXCollections.observableArrayList(
                            new ArrayList<>()
                    )
            );

    public final ObservableList<Rectangle> getPartialResults() {
        return partialResults.get();
    }

    public final ReadOnlyObjectProperty<ObservableList<Rectangle>> partialResultsProperty() {
        return partialResults.getReadOnlyProperty();
    }

    @Override
    protected ObservableList<Rectangle> call() throws Exception {
        updateMessage("Creating Rectangles...");
        for (int i = 0; i < 100; i++) {
            if (isCancelled()) break;
            final Rectangle r = new Rectangle(10, 10);
            r.setX(10 * i);
            Platform.runLater(() -> partialResults.get().add(r));
            updateProgress(i, 100);
        }
        return partialResults.get();
    }
}

При обновлении его наблюдаемых свойств Task сначала проверяет, происходит ли обновление в потоке приложения FX. Если это так, он делает немедленное обновление. Если это не так, то оно завершает обновление в вызове Platform.runLater(). См. Исходный код задачи, чтобы понять, как это делается.

Возможно, можно было бы определить набор общих совместимых свойств, но JavaFX не предоставляет таких возможностей в нем. Действительно, это не нужно. За исключением пакета javafx.concurrent, JavaFX представляет собой однопоточную инфраструктуру пользовательского интерфейса.