简体   繁体   中英

JavaFX splash screen message and progress not updating

I have a JavaFX application that checks for the presence of a database at startup. If the database is not found, a "seed" database must be created before the program can proceed.

Since creating the seed database can be lengthy, I want to display a splash screen and show progress updates as the process proceeds. Only after the seed database is completely written should the program proceed.

Here is the class for the splash screen:

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();
    }
}

When run, the splash screen shows correctly with the image, progress bar and text message area.

The SplashWindow class is called by the following method:

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
    }
}

When run, the splash screen is displayed but never shows the update messages and progress. It shows a wait cursor when the mouse if over the splash screen.

If the try/catch block at the end of the method is commented out, the main program attempts to proceed without waiting for the database to be written. In this case, the splash screen is hidden by the main program window, but does display the update messages as it works. From debugging this, it looks like everything is running on the correct thread -- the database stuff is on a worker thread, the SplashWindow stuff is on the FX event thread.

It seems clear that the call to saveTask.get() is blocking the UI thread, but I am not sure why.

@JewelSea has written a nice alternative that doesn't fit my program architecture very well. It would not be impossible to alter my program to work with his solution.

However, I don't understand why the get() call blocks the UI. What am I doing wrong.

According to the JavaDocs :

( get() ) Waits if necessary for the computation to complete, and then retrieves its result.

Retrieving the computed value without blocking the GUI Thread would be done with getValue() . However: This method only returns a result, when the Task has successfully finished its work. That is why you should do this aysnc in the onSucceeded block.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM