Exploring JavaFX’s Application class

how-to
Apr 15, 201613 mins

Tour JavaFX's Application class and learn about its capabilities

JavaFX applications are based on JavaFX’s Application class. Perhaps you are unfamiliar with this class and have questions about using Application and on what this class offers your application code. This post attempts to answer these questions while exploring Application.

Introducing Application

The javafx.application.Application class provides a framework for managing a JavaFX application. This application must include a class that extends Application, overriding various methods that the JavaFX runtime calls to execute application-specific code.

An application can call Application methods to obtain startup parameters, access host services, arrange to launch itself as a standalone application, interact with the preloader (a small application that’s started before the main application to customize the startup experience), and access the user agent (Web browser) style sheet.

Application life cycle

One of Application‘s tasks is to manage the application’s life cycle. The following overridable Application methods play a role in this life cycle:

  • void init(): Initialize an application. An application may override this method to perform initialization before the application is started. Application‘s init() method does nothing.
  • void start(Stage primaryStage): Start an application. An application must override this abstract method to provide the application’s entry point. The primaryStage argument specifies a container for the user interface.
  • void stop(): Stop an application. An application may override this method to prepare for application exit and to destroy resources. Application‘s stop() method does nothing.

The JavaFX runtime interacts with an application and invokes these methods in the following order:

  1. Create an instance of the class that extends Application.
  2. Invoke init() on the JavaFX Launcher Thread. Because init() isn’t invoked on the JavaFX Application Thread, it must not create javafx.scene.Scene or javafx.stage.Stage objects, but may create other JavaFX objects.
  3. Invoke start() on the JavaFX Application Thread after init() returns and the JavaFX runtime is ready for the JavaFX application to begin running.
  4. Wait for the application to finish. The application ends when it invokes javafx.application.Platform.exit() or when the last window has been closed and Platform‘s implicitExit attribute is set to true.
  5. Invoke stop() on the JavaFX Application Thread. After this method returns, the application exits.

JavaFX creates an application thread, which is known as the JavaFX Application Thread, for running the application’s start() and stop() methods, for processing input events, and for running animation timelines. Creating JavaFX Scene and Stage objects as well as applying scene graph modification operations to live objects (those objects already attached to a scene) must be done on the JavaFX Application Thread.

The java launcher tool loads and initializes the specified Application subclass on the JavaFX Application Thread. If there is no main() method in the Application class, or if the main() method calls Application.launch(), an instance of the Application subclass is constructed on the JavaFX Application Thread.

The init() method is called on the JavaFX Launcher Thread, which is the thread that launches the application; it’s not called on the JavaFX Application Thread. As a result, an application must not construct a Scene or Stage object in init(). However, an application may construct other JavaFX objects in the init() method.

Listing 1 presents a simple JavaFX application that demonstrates this life cycle.

Listing 1. LifeCycle.java

import javafx.application.Application;
import javafx.application.Platform;

import javafx.stage.Stage;

public class LifeCycle extends Application
{
   @Override
   public void init()
   {
      System.out.printf("init() called on thread %s%n", 
                        Thread.currentThread());
   }

   @Override
   public void start(Stage primaryStage)
   {
      System.out.printf("start() called on thread %s%n", 
                        Thread.currentThread());
      Platform.exit();
   }

   @Override
   public void stop()
   {
      System.out.printf("stop() called on thread %s%n", 
                        Thread.currentThread());
   }
}

Compile Listing 1 as follows:

javac LifeCycle.java

Run the resulting LifeCycle.class as follows:

java LifeCycle

You should observe the following output:

init() called on thread Thread[JavaFX-Launcher,5,main]
start() called on thread Thread[JavaFX Application Thread,5,main]
stop() called on thread Thread[JavaFX Application Thread,5,main]

The output reveals that init() is called on a different thread than start() and stop, which are called on the same thread. Because different threads are involved, you may need to use synchronization.

If you comment out Platform.exit(), you won’t observe the stop() called on thread Thread[JavaFX Application Thread,5,main] message because the JavaFX runtime won’t invoke stop() — the application won’t end.

Application parameters

Application provides the Application.Parameters getParameters() method for returning the application’s parameters, which include arguments passed on the command line, unnamed parameters specified in a JNLP (Java Network Launch Protocol) file, and pairs specified in a JNLP file.

Application.Parameters encapsulates the parameters and provides the following methods for accessing them:

  • Map<String,String> getNamed(): Return a read-only map of the named parameters. The map may be empty but is never null. Named parameters include pairs explicitly specified in a JNLP file, and any command-line arguments of the form: --<i>name</i>=<i>value</i>.
  • List<java.lang.String> getRaw(): Return a read-only list of the raw arguments. This list may be empty but is never null. For a standalone application, it’s the ordered list of arguments specified on the command line. For an applet or WebStart application, it includes unnamed parameters as well as named parameters. For named parameters, each pair is represented as a single argument of the form --<i>name</i>=<i>value</i>.
  • List<String> getUnnamed(): Return a read-only list of the unnamed parameters. This list may be empty but is never null. Named parameters (which are represented as pairs) are filtered out.

Listing 2 presents a simple JavaFX application that demonstrates these methods.

Listing 2. Parameters.java

import java.util.List;
import java.util.Map;

import javafx.application.Application;
import javafx.application.Platform;

import javafx.stage.Stage;

public class Parameters extends Application
{
   @Override
   public void start(Stage primaryStage)
   {
      Application.Parameters parm = getParameters();
      System.out.printf("Named parameters: %s%n", parm.getNamed());
      System.out.printf("Raw parameters: %s%n", parm.getRaw());
      System.out.printf("Unnamed parameters: %s%n", parm.getUnnamed());
      Platform.exit();
   }
}

Compile Listing 2 as follows:

javac Parameters.java

Run the resulting Parameters.class as follows:

java Parameters a b c --name=w -name2=x --foo=y -foo=z bar=q

You should observe the following output:

Named parameters: {foo=y, name=w}
Raw parameters: [a, b, c, --name=w, -name2=x, --foo=y, -foo=z, -bar=q]
Unnamed parameters: [a, b, c, -name2=x, -foo=z, -bar=q]

Host services

Application provides the HostServices getHostServices() method for accessing the host services provider, which lets the application obtain its code and document bases, show a Web page in a browser, and communicate with the enclosing Web page using JavaScript when running in a browser.

The javafx.application.HostServices class declares the following methods:

  • String getCodeBase(): Get the code base URI for this application. If the application was launched via a JNLP file, this method returns the codebase parameter specified in the JNLP file. If the application was launched in standalone mode, this method returns the directory containing the application JAR file. If the application is not packaged in a JAR file, this method returns the empty string.
  • String getDocumentBase(): Get the document base URI for this application. If the application is embedded in a browser, this method returns the URI of the Web page containing the application. If the application was launched in WebStart mode, this method returns the the codebase parameter specified in the JNLP file (the document base and the code base are the same in this mode). If the application was launched in standalone mode, this method returns the URI of the current directory.
  • JSObject getWebContext(): Return the JavaScript handle of the enclosing DOM window of the Web page containing this application. This handle is used to access the Web page by calling from Java into JavaScript. If the application is not embedded in a Web page, this method returns null.
  • String resolveURI(String base, String rel): Resolve the specified relative URI against the base URI and return the resolved URI. This method throws java.lang.NullPointerException when either the base or the rel strings are null. It throws java.lang.IllegalArgumentException when there is an error parsing either the base or rel URI strings, or when there is any other error in resolving the URI.
  • void showDocument(String uri): Open the specified URI in a new browser window or tab. The determination of whether it is a new browser window or a tab in an existing browser window will be made by the browser preferences. Note that this will respect the pop-up blocker settings of the default browser; it will not try to circumvent them.

Listing 3 presents a simple JavaFX application that demonstrates most of these methods.

Listing 3. HostServ.java

import javafx.application.Application;
import javafx.application.HostServices;
import javafx.application.Platform;

import javafx.stage.Stage;

public class HostServ extends Application
{
   @Override
   public void start(Stage primaryStage)
   {
      HostServices hs = getHostServices();
      System.out.printf("Code base: %s%n", hs.getCodeBase());
      System.out.printf("Document base: %s%n", hs.getDocumentBase());
      System.out.printf("Web context: %s%n", hs.getWebContext());
      Platform.exit();
   }
}

Compile Listing 3 as follows:

javac HostServ.java

Run the resulting HostServ.class as follows:

java HostServ

You should observe something similar to the following output:

Code base: 
Document base: file:/C:/cpw/javaqa/article19/code/HostServ/
Web context: null

Launching a standalone application

A JavaFX application doesn’t require a main() method. The JavaFX runtime takes care of launching the application and saving command-line arguments. However, if you need to perform various tasks before the application is launched, you can specify a main() method and have it invoke one of the following static methods:

  • void launch(Class<? extends Application> appClass, String... args): Launch a standalone application, where appClass identifies the class that’s constructed and executed by the launcher, and args identifies the command-line arguments that are passed to the application. This method doesn’t return until the application has exited, either via Platform.exit() or by all of the application windows having been closed. It throws java.lang.IllegalStateException when invoked more than once, and throws IllegalArgumentException when appClass doesn’t subclass Application.
  • void launch(String... args): Launch a standalone application. This method is equivalent to invoking the previous method with the Class object of the immediately enclosing class of the method that called launch().

Listing 4 presents a simple JavaFX application that demonstrates the second launch() method.

Listing 4. Launch.java

import javafx.application.Application;
import javafx.application.Platform;

import javafx.stage.Stage;

public class Launch extends Application
{
   @Override
   public void start(Stage primaryStage)
   {
      System.out.printf("start() called on %s%n", Thread.currentThread());
      Platform.exit();
   }

   public static void main(String[] args)
   {
      System.out.printf("main() called on %s%n", Thread.currentThread());
      Application.launch(args);
      System.out.printf("terminating");
   }
}

Compile Listing 4 as follows:

javac Launch.java

Run the resulting Launch.class as follows:

java Launch

You should observe the following output:

main() called on Thread[main,5,main]
start() called on Thread[JavaFX Application Thread,5,main]
terminating

Notifying the preloader with an application-generated notification

An application may contain a preloader that’s used to improve the application-loading experience, especially for applications that are embedded in a browser or launched in WebStart execution mode. The preloader accomplishes the following tasks, notifying the user about what’s happening while the application is loading:

  • receives progress notification about the loading of application resources
  • receives notification of errors
  • receives notification of application initialization and startup
  • decides when the application should become visible

Application declares the following method for interacting with a preloader:

  • void notifyPreloader(Preloader.PreloaderNotification info): Notify the preloader with an application-generated notification. Application code calls this method with the specified notification, which is an object created from the javafx.application package’s Preloader.ErrorNotification, Preloader.ProgressNotification, or Preloader.StateChangeNotification class. The notification is delivered to the preloader’s handleApplicationNotification() method. notifyPreloader() is primarily useful for cases where an application wants the preloader to show progress during a long application initialization step.

Setting and getting the application’s user agent style sheet

JavaFX lets you determine the overall style of your application’s user interface (e.g., controls) by installing a global style sheet. Two style sheets are available, which are represented by the following String constants that are defined in the Application class:

  • STYLESHEET_CASPIAN: The default style sheet in JavaFX 2.x.
  • STYLESHEET_MODENA: The default style sheet in JavaFX 8.x.

If a style sheet is not explicitly installed, a default style sheet (either Caspian or Modena, which is determined by the JavaFX version) is used. Application provides the following static methods for setting a new style sheet and getting the current style sheet:

  • void setUserAgentStylesheet(String url): Set the user agent style sheet for the entire application. This style sheet provides default styling for all user interface controls and other nodes. Each JavaFX release may have a new default style sheet so you would call this method to guarantee consistency. The style sheet is identified by a string-based URL. Presently, you would pass one of the aforementioned Application constants to url. Pass null to restore the platform default style sheet. This method must be called on the JavaFX Application Thread.

    You can also set this property on the command line via option -Djavafx.userAgentStylesheetUrl=[<i>URL</i>]. Setting the property via the command line overrides setting the property via this method.

  • String getUserAgentStylesheet(): Return the user agent style sheet (e.g., CASPIAN) that’s used by the entire application. A null return value means the platform default style sheet is being used. This method must be called on the JavaFX Application Thread.

Listing 5 presents a simple JavaFX application that demonstrates these methods.

Listing 5. GlobalSS.java

import javafx.application.Application;
import javafx.application.Platform;

import javafx.stage.Stage;

public class GlobalSS extends Application
{
    @Override
   public void start(Stage primaryStage)
   {
      System.out.printf("Default global style sheet: %s%n",
                        getUserAgentStylesheet());
      setUserAgentStylesheet(Application.STYLESHEET_CASPIAN);
      System.out.printf("New global style sheet: %s%n",
                        getUserAgentStylesheet());
      setUserAgentStylesheet(Application.STYLESHEET_MODENA);
      System.out.printf("New global style sheet: %s%n",
                        getUserAgentStylesheet());
      Platform.exit();
   }
}

Compile Listing 5 as follows:

javac GlobalSS.java

Run the resulting GlobalSS.class as follows:

java GlobalSS

You should observe the following output:

Default global style sheet: null
New global style sheet: CASPIAN
New global style sheet: MODENA

Conclusion

Application is essential to the architecture of a JavaFX application. In a future post, I’ll demonstrate the applet and Java WebStart application styles of JavaFX applications. Also, I’ll dig into Stage and related classes to address questions about their role in bringing a JavaFX application to visual life.

download
Get the source code for this post’s applications. Created by Jeff Friesen for JavaWorld

The following software was used to develop the post’s code:

  • 64-bit JDK 8u60

The post’s code was tested on the following platform(s):

  • JVM on 64-bit Windows 8.1