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
过于频繁而导致的问题。
我有一个简单的应用程序,只有一个 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
过于频繁而导致的问题。