JavaFX启动屏幕消息和进度未更新
我有一个JavaFX应用程序,它在启动时检查数据库是否存在。如果找不到数据库,则必须先创建“种子”数据库,然后程序才能继续 由于创建种子数据库可能会很长时间,因此我希望显示一个启动屏幕,并在进程进行时显示进度更新。只有在种子数据库完全写入之后,程序才能继续 以下是启动屏幕的类:JavaFX启动屏幕消息和进度未更新,java,multithreading,javafx,Java,Multithreading,Javafx,我有一个JavaFX应用程序,它在启动时检查数据库是否存在。如果找不到数据库,则必须先创建“种子”数据库,然后程序才能继续 由于创建种子数据库可能会很长时间,因此我希望显示一个启动屏幕,并在进程进行时显示进度更新。只有在种子数据库完全写入之后,程序才能继续 以下是启动屏幕的类: public final class SplashWindow { private final Label message; private final ProgressBar progress;
public final class SplashWindow {
private final Label message;
private final ProgressBar progress;
private final Stage splashStage;
public SplashWindow() {
Image img = new Image(IMAGE_PREFIX + "splash_image.png");
double imgWidth = img.getWidth();
ImageView splashImage = new ImageView(img);
splashImage.setFitWidth(imgWidth);
splashImage.setPreserveRatio(true);
message = new Label("Saving seed database...");
message.setPrefWidth(imgWidth);
message.setAlignment(Pos.CENTER);
progress = new ProgressBar();
progress.setPrefWidth(imgWidth);
Pane splashVBox = new VBox(3);
splashVBox.setPadding(new Insets(5));
splashVBox.getChildren().addAll(splashImage, progress, message);
splashStage = new Stage(StageStyle.UTILITY);
splashStage.setScene(new Scene(splashVBox));
}
public void bindMessageProperty(ReadOnlyStringProperty sp) {
message.textProperty().bind(sp);
}
public void bindProgressProperty(ReadOnlyDoubleProperty dp) {
progress.progressProperty().bind(dp);
}
public void show() {
splashStage.show();
}
public void shutdown() {
message.textProperty().unbind();
progress.progressProperty().unbind();
splashStage.hide();
}
}
运行时,启动屏幕正确显示图像、进度条和文本消息区域
通过以下方法调用SplashWindow
类:
private void saveSeedDatabase(ObservableList<RefModel> docList) {
SplashWindow splash = new SplashWindow();
Task<Integer> saveTask = new Task<Integer>() {
@Override
protected Integer call() throws InterruptedException {
updateMessage("Saving references...");
int docsSaved = 0;
for (RefModel rm : docList) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
updateMessage("Saving: " + rm.getTitle());
saveNewReference(rm);
docsSaved++;
updateProgress(docsSaved, docList.size());
}
updateMessage("Saved " + docsSaved + " references to database");
return docsSaved;
}
};
saveTask.setOnSucceeded((WorkerStateEvent t) -> {
splash.shutdown();
});
splash.bindMessageProperty(saveTask.messageProperty());
splash.bindProgressProperty(saveTask.progressProperty());
splash.show();
new Thread(saveTask).start();
try {
saveTask.get();
} catch (InterruptedException | ExecutionException ex) {
// Do nothing
}
}
private void saveSeedDatabase(可观察列表docList){
SplashWindow splash=新的SplashWindow();
Task saveTask=新任务(){
@凌驾
受保护的整数调用()引发InterruptedException{
更新消息(“保存引用…”);
int docsSaved=0;
用于(参考型号rm:docList){
如果(isCancelled()){
更新消息(“已取消”);
打破
}
updateMessage(“保存:+rm.getTitle());
saveNewReference(rm);
docsSaved++;
updateProgress(docsSaved,docList.size());
}
updateMessage(“保存的”+文档保存的+“对数据库的引用”);
返回已保存的文档;
}
};
saveTask.setOnSucceeded((WorkerStateEvent t)->{
splash.shutdown();
});
splash.bindMessageProperty(saveTask.messageProperty());
splash.bindProgressProperty(saveTask.progressProperty());
splash.show();
新线程(saveTask.start();
试一试{
saveTask.get();
}捕获(InterruptedException | ExecutionException ex){
//无所事事
}
}
运行时,会显示启动屏幕,但不会显示更新消息和进度。当鼠标悬停在启动屏幕上时,将显示等待光标
如果方法末尾的try/catch块被注释掉,主程序将尝试继续,而不等待写入数据库。在这种情况下,启动屏幕被主程序窗口隐藏,但在工作时会显示更新消息。通过调试,看起来一切都在正确的线程上运行--数据库内容在工作线程上,SplashWindow
内容在FX事件线程上
显然,调用saveTask.get()
会阻塞UI线程,但我不确定原因
已经编写了一个不太适合我的程序架构的。要改变我的计划以配合他的解决方案并非不可能
但是,我不明白为什么get()
调用会阻塞UI。我做错了什么。根据:
(get()
)在必要时等待计算完成,然后检索其结果
在不阻塞GUI线程的情况下检索计算出的值将使用。但是:此方法仅在
任务
成功完成其工作时返回结果。这就是为什么你应该在街区里做这件事。另请看@James\u D:本周早些时候的文章。很不错的。谢谢你的支持。不,使用getValue()
方法仍然可以让程序在写入数据库之前继续运行。我想阻止saveSeedDatabase()
方法返回,直到任务完成并写入数据库,但不冻结对初始屏幕的更新。关键是您在FX应用程序线程上调用saveSeedDatabase
(您必须这样做,因为它创建并显示了一个阶段
)。因此,如果您在该方法中阻塞(“在任务完成之前阻止该方法返回”),则必然会阻塞FX应用程序线程。任务完成后需要执行的任何操作都必须在onSucceeded
处理程序中完成。