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

时间:2018-07-04 10:31:49

标签: javafx javafx-8

我有一个带有单个JavaFX窗口的简单应用程序。我正在将数据发送到for循环内的Azure IoTHub中。此for循环在JavaFX Task中,并且for循环的延迟很小(Thread.sleep(300)),因此可以在UI上显示进度。我有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
}

这是我的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();
  }

运行应用程序时,我在IllegalStateException: Not on FX application thread方法内不断获得updateValue异常。据我了解的文档,updateValue方法的全部要点是在应用程序线程上运行,并且可以用来传递自定义对象,该对象可以用来更新UI。

那我做错了什么?

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

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 :(得分:2)

updateValue不会自动在应用程序线程上运行,因此不必在应用程序线程上运行它,因为它负责更新应用程序线程上value的{​​{1}}属性

您覆盖版本Task中的代码在后台线程上执行逻辑,但是该逻辑需要在应用程序线程上运行:

updateValue

由于上述代码在后台线程上运行,因此绑定会导致dataHelper.setDate(value.getDate()); dataHelper.setCount(value.getCount()); 属性从后台线程更新。

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

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

Task<DataHelper> task

您也可以使用task.valueProperty().addListener((o, oldValue, newValue) -> { if (newValue != null) { dateValue.setText(newValue.getDate()); countValue.setText(newValue.getCount()); } }); 进行这些更新,因为它们更新的频率不高,不会导致使用Platform.runLater过于频繁而导致的问题。