JavaFX任务updateValue在FX应用程序线程上引发IllegalStateException:非

JavaFX任务updateValue在FX应用程序线程上引发IllegalStateException:非,javafx,javafx-8,Javafx,Javafx 8,我有一个简单的应用程序,只有一个JavaFX窗口。我正在将数据发送到for循环中的Azure IoTHub。这个for循环位于JavaFX任务中,for循环有一个很小的延迟(Thread.sleep(300)),因此可以在UI上显示进度。我有两个标签,我想在数据传输过程中更新,始终显示最新发送的数据。我为此提供了以下帮助器类: public class DataHelper { private StringProperty date = new SimpleStringProperty()

我有一个简单的应用程序,只有一个JavaFX窗口。我正在将数据发送到for循环中的Azure IoTHub。这个for循环位于JavaFX任务中,for循环有一个很小的延迟(Thread.sleep(300)),因此可以在UI上显示进度。我有两个标签,我想在数据传输过程中更新,始终显示最新发送的数据。我为此提供了以下帮助器类:

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方法:

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

(138是行Task Task=newtask(),155 updateValue(dh);,166 dataHelper.setDate(value.getDate());)
updateValue
不会自动在应用程序线程上运行,也不必在应用程序线程上运行它,因为它负责更新应用程序线程上任务的
属性

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

dataHelper.setDate(value.getDate());
dataHelper.setCount(value.getCount());
绑定会导致从后台线程更新
文本
属性,因为上面的代码在后台线程上运行

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

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

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

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

谢谢,这很有效。所以这个监听器在每次调用updateValue()时都会触发,对吗?不一定<代码>任务
使用
原子引用
保留最新的值,但
平台。用于更新的runLater
实际上在某个未指定的时间运行。这意味着您可能看不到属性的每个中间值。(请注意,
call
的返回值也用于最后一次更新
value
属性;这就是我添加
null
检查的原因)
task.valueProperty().addListener((o, oldValue, newValue) -> {
    if (newValue != null) {
        dateValue.setText(newValue.getDate());
        countValue.setText(newValue.getCount());
    }
});