Задача JavaFX updateValue создает исключение IllegalStateException: отсутствует в потоке приложения FX - PullRequest
0 голосов
/ 04 июля 2018

У меня есть простое приложение с одним окном JavaFX. Я отправляю данные в Azure IoTHub внутри цикла for. Этот цикл for входит в задачу JavaFX, а цикл for имеет небольшую задержку (Thread.sleep (300)), поэтому прогресс может отображаться в пользовательском интерфейсе. У меня есть 2 метки, которые я хочу обновить во время передачи данных, всегда показывая последние отправленные данные. У меня есть следующий вспомогательный класс для этого:

public class DataHelper {

  private StringProperty date = new SimpleStringProperty();
  private StringProperty count = new SimpleStringProperty();

  public DataHelper() {
  }

  public DataHelper(String date, String count) {
    this.date.setValue(date);
    this.count.setValue(count);
  }

  //getters  and setters
}

А вот мой метод sendErrorsToHub внутри моего класса контроллера UI:

  private void sendErrorsToHub(List<TruckErrorForCloud> toCloud) {
    DataHelper dataHelper = new DataHelper("", "");
    Task task = new Task<DataHelper>() {
      @Override
      public DataHelper call() {
        try {
          int i = 0;
          for (TruckErrorForCloud error : toCloud) {
            Thread.sleep(300);
            i++;
            String strMessage = Utility.toPrettyJson(null, error);
            if (strMessage != null) {
              Message msg = new Message(strMessage);
              msg.setMessageId(java.util.UUID.randomUUID().toString());
              client.sendEventAsync(msg, null, null);
            }
            updateProgress(i, toCloud.size());
            DataHelper dh = new DataHelper(error.getErrorTimeStamp().substring(0, error.getErrorTimeStamp().length() - 9),
                                           String.valueOf(error.getCount()));
            updateValue(dh);
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
        return null;
      }

      @Override
      protected void updateValue(DataHelper value) {
        super.updateValue(value);
        dataHelper.setDate(value.getDate());
        dataHelper.setCount(value.getCount());
      }

      //succeeded method omitted
    };
    dateValue.textProperty().bind(dataHelper.dateProperty());
    countValue.textProperty().bind(dataHelper.countProperty());
    progressBar.progressProperty().bind(task.progressProperty());
    new Thread(task).start();
  }

Когда я запускаю приложение, я постоянно получаю IllegalStateException: Not on FX application thread исключений внутри метода updateValue. Насколько я понимаю, вся суть метода updateValue в том, что он работает в потоке приложения и может использоваться для передачи пользовательского объекта, который можно использовать для обновления пользовательского интерфейса.

Что я тогда делаю не так?

Нижняя часть трассировки стека с моими классами выглядит следующим образом:

at eu.mantis.still_rca_simulator.gui.DataHelper.setDate(DataHelper.java:28)
at eu.mantis.still_rca_simulator.gui.GuiController$1.updateValue(GuiController.java:166)
at eu.mantis.still_rca_simulator.gui.GuiController$1.call(GuiController.java:155)
at eu.mantis.still_rca_simulator.gui.GuiController$1.call(GuiController.java:138)

(138 - это строка Task task = new Task (), 155 updateValue (dh) ;, 166 dataHelper.setDate (value.getDate ());)

1 Ответ

0 голосов
/ 04 июля 2018

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

Ваш код в переопределенной версии updateValue выполняет логику в фоновом потоке, который должен быть запущен в потоке приложения:

dataHelper.setDate(value.getDate());
dataHelper.setCount(value.getCount());

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

В этом случае я рекомендую использовать неизменяемый класс DataHelper и обновлять пользовательский интерфейс с помощью слушателя до свойства value:

Удалите переопределение updateValue и локальную переменную dataHelper, инициализируйте графический интерфейс с пустыми строками, при необходимости объявите task как Task<DataHelper> task и выполните следующие действия для обновления графического интерфейса:

task.valueProperty().addListener((o, oldValue, newValue) -> {
    if (newValue != null) {
        dateValue.setText(newValue.getDate());
        countValue.setText(newValue.getCount());
    }
});

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

...