JavaFX Task updateValue 抛出 IllegalStateException:不在 FX 应用程序线程上

JavaFX Task updateValue throws IllegalStateException: Not on FX application thread

我有一个简单的应用程序,只有一个 JavaFX window。我在 for 循环内将数据发送到 Azure IoTHub。此 for 循环在 JavaFX 任务中,并且 for 循环有一个小延迟 (Thread.sleep(300)),因此可以在 UI 上显示进度。我有 2 个标签要在数据传输期间更新,始终显示最新发送的数据。为此,我有以下助手 class:

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
}

这是我的 UI 控制器中的 sendErrorsToHub 方法 class:

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

当我 运行 应用程序时,我在 updateValue 方法中不断得到 IllegalStateException: Not on FX application thread 异常。据我对文档的理解,updateValue 方法的全部要点,它 运行s 在应用程序线程上,它可用于传递自定义对象,该对象可用于更新UI。

那我做错了什么?

我的 classes 的堆栈跟踪底部如下:

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

(138 是行 Task task = new Task(), 155 updateValue(dh);, 166 dataHelper.setDate(value.getDate());)

updateValue 不会在应用程序线程上自动 运行 并且没有必要在应用程序线程上 运行 它因为它负责更新 value 属性 的 Task 在应用程序线程上。

覆盖版本 updateValue 中的代码在后台线程上执行需要在应用程序线程上 运行 的逻辑:

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

绑定导致 text 属性从后台线程更新,因为上述代码 运行s 在后台线程上。

在这种情况下,我建议使用不可变 DataHelper class 并使用侦听器更新 ui value 属性:

删除 updateValue 覆盖和 dataHelper 局部变量,用空字符串初始化 gui,如有必要,将 task 声明为 Task<DataHelper> task并执行以下操作以更新 gui:

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

您也可以使用 Platform.runLater 进行这些更新,因为它们发生的频率不够高,不会导致可能因使用 Platform.runLater 过于频繁而导致的问题。