我有一个JavaFX应用程序,可以在启动时检查数据库是否存在。如果找不到数据库,那么#"种子"必须在程序继续之前创建数据库。
由于创建种子数据库可能很长,因此我希望在进程继续时显示启动画面并显示进度更新。只有在完全写入种子数据库之后,程序才能继续。
以下是启动画面的类:
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
}
}
运行时,会显示启动画面,但从不显示更新消息和进度。当鼠标悬停在启动画面上时,它会显示等待光标。
如果方法末尾的try / catch块被注释掉,主程序将尝试继续而不等待写入数据库。在这种情况下,启动屏幕被主程序窗口隐藏,但在它工作时会显示更新消息。从调试开始,看起来一切都在正确的线程上运行 - 数据库的东西在工作线程上,SplashWindow
的东西在FX事件线程上。
很明显,对saveTask.get()
的调用阻止了UI线程,但我不确定原因。
@JewelSea写了nice alternative,并不能很好地适应我的程序架构。改变我的程序以使用他的解决方案是不可能的。
但是,我不明白为什么get()
调用阻止了用户界面。我做错了什么。
答案 0 :(得分:2)
根据JavaDocs:
(
get()
)如果需要等待计算完成,然后检索其结果。
在不阻止GUI线程的情况下检索计算值将使用getValue()
完成。但是:当Task
成功完成工作时,此方法仅返回结果。这就是你应该在onSucceeded
块中执行此操作的原因。