4

I've just written a JavaFX application and realized I don't really understand how it actually starts up.

@Override
public void start(Stage primaryStage) throws Exception {
    Parent root = new FXMLLoader(this.getClass().getResource("view.fxml")).load();
    primaryStage.setTitle("Dice Roller");
    primaryStage.setScene(new Scene(root));
    primaryStage.show();
}

public static void main(String[] args) {
   launch(args);
}

This is the template given to me in Intellij. My entry point would be main where the only thing that gets run is launch(args). I've tried digging in the Application class but didn't find anything that would point towards running the start method. How is this even launched? Since JavaFX has it's own thread I'm assuming a thread gets created in the launch method and the main thread doesn't return from launch until you call Platform.exit or just close all windows. This all feels just a bit too abstract to me right now. Can someone explain to me how it all fits together?

J-Alex
  • 6,881
  • 10
  • 46
  • 64
Tryphon
  • 161
  • 1
  • 13
  • 3
    This might be useful https://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html – iamwhoiam Jul 03 '17 at 10:38
  • 2
    Another more detailed article, with examples: http://www.javaworld.com/article/3057072/learn-java/exploring-javafxs-application-class.html You can use those examples to come up with your own "experiments" to observe the lifecycle. – Itai Jul 03 '17 at 10:58
  • Related: [*I Want to Understand How abstract class Application's launch() method work in JavaFX*](https://stackoverflow.com/q/76798326/642706) – Basil Bourque Aug 08 '23 at 04:47

2 Answers2

3

Below is what your main application class must look like. I named it "HelloApp" because I needed a name. I also changed launch with Application.launch, it's the same but less confusing (see explanations below).

Please note however (thanks @Cypher in comments for bringing this up) that java does not need the good old public static void main(String[] args) method to run a JavaFX application.
If you omit the main method from below, compile it, and run it with java HelloApp, it will work, and might be slightly less confusing :)
You can still handle command line arguments without main, as they are passed to the parameters object, which you can obtain from within your Application with getParameters().

My IDE also allows to run an Application without main, and apparently most do.

That being said, let's see what happens here in your case:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class HelloApp extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = new FXMLLoader(this.getClass().getResource("view.fxml")).load();
        primaryStage.setTitle("Dice Roller");
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

}
  • HelloApp.main() invokes launch()
  • launch is a static method in Application class that you extends, so you can use launch() directly but when calling static methods it's recommended to use the class name, so I replaced above with Application.launch. Some IDEs and linters warn you with "java static method should be accessed in a static way" if you don't do that.
  • Within the launch method, the JavaFX runtime (below "JavaFX") figures out which class is the caller (by using Thread.currentThread().getStackTrace(), but that's just an interesting implementation detail). That caller class is expected to be a javafx.application.Application
    -- When running without main, the above is irrelevant, and JavaFX starts about here. --
  • JavaFX creates an application thread for running the application start method, processing input events, and running animation timelines.
  • JavaFX constructs an instance of the specified Application class
  • JavaFX calls init() (which you can override)
  • JavaFX calls start() (which you must implement yourself), from the "JavaFX Application Thread"

The rest of the lifecycle is described in Application javadoc -- subsequent steps are waiting for application to finish (either app calls Platform.exit(), or last window has been closed and implicitExit is true), then JavaFX calls stop() (which you can override).

Hugues M.
  • 19,846
  • 6
  • 37
  • 65
  • It might be worth noting that the static `main` method is optional in the majority of use cases. There are some places where it's required, such as non-standard or out-dated launchers (winrun4j comes to mind). – Cypher Jul 03 '17 at 18:36
  • Good point, I was initially triggered by this static-method-not-invoked-statically, and forgot about that, will edit in a minute (although my knowledge about what tools do not support this is quite limited, I know `java` runs an `Application` without main... and my IDE also supports that) – Hugues M. Jul 03 '17 at 18:42
2

The start() method of a JavaFX application is called via this call stack:

  • Application#launch:252
  • LauncherImpl#launchApplication:143
  • LauncherImpl#launchApplication:182
  • LauncherImpl#launchApplication1:863
  • Application#start (overridden by your application)
Luciano van der Veekens
  • 6,307
  • 4
  • 26
  • 30
  • So it's as I suspected, it creates a thread in the Application class and invokes the start method. So in effect you lose the main thread your program was running(launch doesn't return until you quit, as per the documentation of Application) on if you do it like the template gives you. Wouldn't it be better if I specified my own thread to the JavaFX stack and passed that? – Tryphon Jul 03 '17 at 11:54
  • 2
    @Tryphon It wouldn't work if you tried to use *any* thread: `start()` must run on the same thread that event handling and UI rendering is performed on (i.e. on the FX Application Thread). Swing did things the other way around (sort of as you describe), in that you have to explicitly specify that the UI initialization is performed on the AWT thread using `SwingUtilities.invokeLater(...)`. The result? Lots of broken code that is incorrectly threaded, and lots of bulletproofing in the Swing library code to try to minimize the impact of incorrect client code... – James_D Jul 03 '17 at 12:09