JavaFX: How to easily implement an application preloader

28.9.2015 | 4 minutes of reading time


Have you ever been in situation that you developed an awesome JavaFX application but it is taking too long to initially load due to non-JavaFX prerequisites?

Maybe you are waiting for the connection to the database to initialize, checking for updates, testing connection or retrieving data from server…

Common application launch

The most basic approach for launching JavaFX applications is by invoking an Application.launch() method inside main method, which normally looks like this:

1public class MyApplication extends Application {
2   public static void main(String[] args) {
3      launch(args);
4   }
6   @Override
7   public void start(Stage primaryStage) throws Exception {
8      // Do some heavy lifting
9      // Load primary stage
10   }

Because the Application#start method is invoked on JavaFX Application Thread, the “heavy lifting” is going to cause a long delay between the moment user has ran the application and the moment the application window is actually shown.

Launch with preloader

If we browse through the source code of the Application.launch() method we can see that under the hood LauncherImpl#launchApplication is called. Going deeper inside LauncherImpl we can see that actually the private static method launchApplication1 is called with preloaderClass argument set to null. This doesn’t help us much if we wish to use the preloader, so we are going to make a small shortcut and directly use the launchApplication(appClass, preloaderClass, args) method of LauncherImpl in order to launch the JavaFX application from main method. Let’s look at header of launchApplication:

2* This method is called by the standalone launcher.
3* It must not be called more than once or an exception will be thrown.
5* Note that it is always called on a thread other than the FX application
6* thread, since that thread is only created at startup.
8* @param appClass application class
9* @param preloaderClass preloader class, may be null
10* @param args command line arguments
12public static void launchApplication(final Class<? extends Application> appClass,
13   final Class<? extends Preloader> preloaderClass,
14   final String[] args) {

Fine, we now know how to launch JavaFX application with a preloader, so let’s start with some example Main class:

1import com.sun.javafx.application.LauncherImpl;
3public class Main {
4   public static void main(String[] args) {
5      LauncherImpl.launchApplication(MyApplication.class, MyPreloader.class, args);
6   }

In this example the MyApplication class extends javafx.application.Application and implements its #start and #init methods, let us consider a simple example:

1import javafx.application.Application;
2import javafx.scene.Scene;
3import javafx.scene.control.Label;
4import javafx.scene.layout.BorderPane;
5import javafx.stage.Stage;
6public class MyApplication extends Application {
8   @Override
9   public void init() throws Exception {
10      // Do some heavy lifting
11   }
13   @Override
14   public void start(Stage primaryStage) throws Exception {
15      BorderPane root = new BorderPane(new Label("Loading complete!"));
16      Scene scene = new Scene(root);
17      primaryStage.setWidth(800);
18      primaryStage.setHeight(600);
19      primaryStage.setScene(scene);
20      primaryStage.show();
21   }

It can be easily observed that the “heavy lifting” is moved to #init instead of #start. We will be later back to this, let’s consider how preloader should look like first:

1import javafx.application.Preloader;
2import javafx.application.Preloader.StateChangeNotification.Type;
3import javafx.scene.Scene;
4import javafx.scene.control.Label;
5import javafx.scene.control.ProgressBar;
6import javafx.scene.layout.BorderPane;
7import javafx.scene.layout.Region;
8import javafx.scene.layout.VBox;
9import javafx.stage.Stage;
10public class MyPreloader extends Preloader {
11private Stage preloaderStage;
13    @Override
14    public void start(Stage primaryStage) throws Exception {
15       this.preloaderStage = primaryStage;
17       VBox loading = new VBox(20);
18       loading.setMaxWidth(Region.USE_PREF_SIZE);
19       loading.setMaxHeight(Region.USE_PREF_SIZE);
20       loading.getChildren().add(new ProgressBar());
21       loading.getChildren().add(new Label("Please wait..."));
23       BorderPane root = new BorderPane(loading);
24       Scene scene = new Scene(root);
26       primaryStage.setWidth(800);
27       primaryStage.setHeight(600);
28       primaryStage.setScene(scene);
29       primaryStage.show();
30   }
32   @Override
33   public void handleStateChangeNotification(StateChangeNotification stateChangeNotification) {
34      if (stateChangeNotification.getType() == Type.BEFORE_START) {
35         preloaderStage.hide();
36      }
37   }

What we wish to achieve

What the given diagram is describing is basically to show the preloader stage until our application is performing the “heavy lifting” and showing the main application scene when this work is done. The interesting thing about the JavaFX launch flow is that the process in which application is started are organized precisely in the following steps:

1. MyPreloader constructor called, thread: JavaFX Application Thread
2. MyPreloader#init (could be used to initialize preloader view), thread: JavaFX-Launcher
3. MyPreloader#start (showing preloader stage), thread: JavaFX Application Thread
5. MyApplication constructor called, thread: JavaFX Application Thread
7. MyApplication#init (doing some heavy lifting), thread: JavaFX-Launcher
9. MyApplication#start (initialize and show primary application stage), thread: JavaFX Application Thread

Notice that constructors and start methods are called on the JavaFX Application Thread allowing interaction with UI components. To interact with the UI during init phase, use Platform.runLater(…) to schedule execution of code on JavaFX Application Thread.

For a better understanding of this simple example of creating your own JavaFX application preloader, please take a look at provided source code.
Source code for the example is available here .
The Java version used for example is 1.8.0_60.

share post




More articles in this subject area\n

Discover exciting further topics and let the codecentric world inspire you.


Gemeinsam bessere Projekte umsetzen

Wir helfen Deinem Unternehmen

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.