我有一个带有单个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());)
答案 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
过于频繁而导致的问题。