代码之家  ›  专栏  ›  技术社区  ›  logi0517

JavaFX任务UpdateValue抛出IllegalStateException:不在FX应用程序线程上

  •  0
  • logi0517  · 技术社区  · 6 年前

    我有一个简单的应用程序,只有一个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控制器类中的senderRorrorstoHub方法:

      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 方法。就我所理解的文档而言, 更新值 方法,它在应用程序线程上运行,并可用于传递可用于更新UI的自定义对象。

    那我做错什么了?

    stacktrace和我的类的底部如下:

    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 回复  |  直到 6 年前
        1
  •  2
  •   fabian    6 年前

    updateValue 不会自动在应用程序线程上运行,也不必在应用程序线程上运行,因为它负责更新 value 财产 Task 在应用程序线程上。

    重写版本中的代码 更新值 在后台线程上执行需要在应用程序线程上运行的逻辑:

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

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

    在这种情况下,我建议使用不可变的 DataHelper 使用侦听器初始化并更新ui 价值 属性:

    移除 更新值 覆盖和 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 对于这些更新,因为它们发生的频率不足以导致使用 平台.runlater 太频繁了。