我正在尝试在 JavaFX 中创建一个应用程序,该应用程序将从我的 SQLITE 数据库检索数据并显示它。但即使我尝试使用
Task
和 Platform.runLater()
并使用 Thread.sleep()
模拟加载,它仍然会冻结我的应用程序。我是 javaFX 并发新手,但我正在尝试阅读文档(JavaFXDocs),但似乎找不到我的解决方案。
这是代码
@FXML
private void showAllData() throws SQLException, IOException {
Task<Void> task = dataShowAsATask();
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
private Task<Void> dataShowAsATask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
Platform.runLater(() -> {
try {
clearDataResultHolder();
dataResultHolder.getChildren().addAll(getArrayOfUserAsNode());
/* Simulate loading */
Thread.sleep(2 * 1000);
} catch (SQLException | IOException | InterruptedException e) {
throw new RuntimeException(e);
}
});
return null;
}
};
}
private Node getUserAsANode(User user) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/prince/firstcrudtest/views/card/CardSkeleton.fxml"));
Node data = fxmlLoader.load();
CardSkeletonController controller = fxmlLoader.getController();
setController(controller, user);
return data;
}
private AbstractList<Node> getArrayOfUserAsNode() throws SQLException, IOException {
ArrayList<Node> result = new ArrayList<>();
ArrayList<User> data = dbNav.getAllData();
for (User user : data) {
result.addLast(getUserAsANode(user));
}
return result;
}
private void setController(CardSkeletonController controller, User user) {
controller.setIdLabel(String.valueOf(user.getId()));
controller.setUsernameLabel(user.getUserName());
controller.setPasswordLabel(Arrays.toString(user.getPassword()));
controller.setSaltLabel(Arrays.toString(user.getSalt()));
}
private void clearDataResultHolder() {
dataResultHolder.getChildren().clear();
}
我真的不知道该怎么办,因为我是 JavaFX 和 Java 线程的新手。
在非 JavaFX 应用程序线程的线程中执行耗时操作的原因是它们会延迟 JavaFX 事件的处理,包括渲染场景以及处理鼠标和键盘输入。
Platform.runLater 专门用于在 JavaFX 应用程序线程中运行任务。使用它来更新场景(这是clearDataResultHolder()和dataResultHolder.getChildren().addAll(getArrayOfUserAsNode())所做的)是正确的。
但是,在JavaFX应用程序线程中调用Thread.sleep是有害的。这就是冻结您的申请的原因。
基于执行长时间运行操作的任务创建一个新线程是很好的,因为该新线程不是 JavaFX 应用程序线程,并且不会挂起应用程序线程。您应该在该任务中执行 Thread.sleep before 调用 Platform.runLater:
@Override
protected Void call() throws Exception {
// Does the time-consuming work in this background thread,
// NOT in the application thread.
/* Simulate loading */
Thread.sleep(2 * 1000);
// Updates the user interface in the JavaFX application thread,
// because updating it in this background thread is not allowed.
Platform.runLater(() -> {
clearDataResultHolder();
dataResultHolder.getChildren().addAll(getArrayOfUserAsNode());
});
return null;
}
如果您实际上执行数据库操作,而不是仅仅调用 Thread.sleep,则代码结构将是相同的:在任务线程中执行工作,然后调用 Platform.runLater 用您检索到的数据更新场景。