Search code examples
javajavafxterminate

JavaFX program terminates when Stage.show() called a second time


I'm writing a JavaFX program that doesn't show the stage (Stage.show()) in the start(Stage) function, but rather, other methods in the same class call Stage.show() to show the stage at a later time. If I try to call any of the methods which call Stage.show() more than once, the program terminates itself.

The first showing of the stage/window works as expected, and after I use the ✕ to close the window the program proceeds correctly. However, if I try to call Stage.show() again, even from the same method, the stage/window opens and immediately closes, terminating the entire program with it. There is no error: I'm using Eclipse and the exit code is 0, suggesting successful program completion. I don't know what is different the second time that makes the program think it should terminate.

My program is directed by a command system that I built into it, and the command system is paused during the opening of the window or else the window crashes. Because of this, I have no other way of closing the window than the ✕. The Stage.setonHidden() restarts my command system.

Any insights into why closing and reopening a stage might cause the entire program to terminate would be appreciated.

This is a whittled down version of my program:

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.layout.BorderPane;
    import javafx.stage.Stage;
    
    public class Example extends Application{
        
        static BorderPane mainPane = new BorderPane();
        static Scene scene;
        static Stage stage;
    
        public static void main(String[] args) {
            launch(args);
        }
        
        public void start(Stage newStage) {
            stage = newStage;
            scene = new Scene(mainPane, 640, 700);
            stage.setScene(scene);
            stage.setOnHidden(e -> showBuild()); //Runs successfully when window is closed
            showBuild();
        }
    
        public static void showBuild() {
            stage.show(); //fails to show stage and terminates program when called a second time
        }
    
    }

Solution

  • There are two distinct issues here. The first (and the one I understand best) is that the JavaFX platform, by default, will exit when the last window is closed. You can avoid this with a call to

    Platform.setImplicitExit(false);
    

    In this case, you will need to explicitly call

    Platform.exit();
    

    to terminate the application.

    The second appears to be a question of timing. It looks like the onHidden event handler is getting closed before the window has properly closed. Consequently, you call stage.show() but then subsequently the underlying native peer window is closed, causing the window not to appear. This might be a bug.

    A couple of workarounds:

    1. Wrap the call in Platform.runLater(...) this will enqueue the call on the FX Application Thread and it won't get invoked until after pending event handlers have completed. This ensures the call to stage.show() happens after the window has completely closed.

       public void start(Stage newStage) {
           Platform.setImplicitExit(false);
           stage = newStage;
           scene = new Scene(mainPane, 640, 700);
           stage.setScene(scene);
           stage.setOnHidden(e -> Platform.runLater(Example::showBuild)); 
           showBuild();
      
       }
      
    2. Use the showingProperty() instead. This appears to change only after the window has completely closed.

       public void start(Stage newStage) {
           Platform.setImplicitExit(false);
           stage = newStage;
           scene = new Scene(mainPane, 640, 700);
           stage.setScene(scene);
           stage.showingProperty().addListener((obs, wasShowing, isNowShowing) -> {
               if (! isNowShowing) {
                   showBuild();
               }
           });
           showBuild();
      
       }