JavaFX启动画面消息和进度未更新

时间:2015-06-18 13:51:03

标签: java multithreading javafx

我有一个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()调用阻止了用户界面。我做错了什么。

1 个答案:

答案 0 :(得分:2)

根据JavaDocs

  

get())如果需要等待计算完成,然后检索其结果。

在不阻止GUI线程的情况下检索计算值将使用getValue()完成。但是:当Task成功完成工作时,此方法仅返回结果。这就是你应该在onSucceeded块中执行此操作的原因。