javajavafxshutdownshutdown-hookapplication-shutdown

Using JavaFX Application.stop() method over Shutdownhook


So im using shutdownhook to clean up. But since its not always guaranteed that shutdownhooks thread executes, should i just push this code onto JavaFX Application Thread (method stop()), which executes every time i close my application? Code is not expensive to run. Its basically just close socket if not closed and kill processs if not killed.

Is it good practice to use Application.stop() to cleanup over ShutdownHook?

Quote from doc:

This method is called when the application should stop, and provides a convenient place to prepare for application exit and destroy resources. The implementation of this method provided by the Application class does nothing.

NOTE: This method is called on the JavaFX Application Thread.

Is this method only intended to clean up resources of UI? So far i don't see a reason using shutdownhook over stop() at all.


Solution

  • stop() will only be called if you exit the application via Platform.exit() (or when the last window is closed if Platform.implicitExit is true). Shutdown hooks will execute if System.exit() is called, or if the native process running the JVM is interrupted (e.g. ctrl-C on a *nix-like OS), in addition to the usual way of exiting a JavaFX Application.

    Note that it appears to be important to register the shutdown hook in the main thread, before calling Application.launch().

    stop() is executed on the FX Application Thread, so it is safe to access UI elements (e.g. to show "save unsaved changes" dialogs, etc). Shutdown hooks are run in a background thread, so cannot access UI elements (indeed the FX Toolkit will probably long have stopped running by that stage).

    So the choice depends on the use case.

    To make this a little more concrete, here's a quick test class:

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.HBox;
    import javafx.stage.Stage;
    
    public class ShutdownTest extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            Button platformExit = new Button("Platform Exit");
            platformExit.setOnAction(e -> Platform.exit());
            Button systemExit = new Button("System Exit");
            systemExit.setOnAction(e -> System.exit(0));
            Button hang = new Button("Hang");
            hang.setOnAction(e -> {while(true);});
            HBox root = new HBox(5, platformExit, systemExit, hang);
            root.setPadding(new Insets(20));
            root.setAlignment(Pos.CENTER);
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
    
        }
    
        @Override
        public void stop() {
            System.out.println("Stop");
        }
    
        public static void main(String[] args) {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Shutdown hook")));
            launch(args);
        }
    }
    

    I tested this on Mac OS X.

    Exiting via the "Platform Exit" button, by closing the window, or by right-clicking on the Dock and choosing "Quit" will execute both the stop() method and the shutdown hook.

    Exiting by the "System Exit" button, by forcing the process to quit from "Activity Monitor", or by killing the process by kill id from the command line, will execute the shutdown hook only. Hanging the application by pressing the "Hang" button and then right-clicking on the Dock and choosing "Force Quit" has the same result.

    Exiting by sending a SIGKILL to the process (kill -9 id or kill -SIGKILL id from the command line) executes neither the stop() method nor the shutdown hook.