billdoor billdoor - 5 months ago 89
Java Question

JavaFX launch Application standalone OR from another application

following Scenario:

JavaFxMainApp
JavaFXUpdaterApp

Both are JavaFX applications with a GUI and a void main() method.


  1. The Updater has to be able to start the JavaFXMainApp by accessing
    the JavaFxMainApp.jar ONLY knowing about the main class -> it can only! call
    main().

  2. The JavaFxMainApp has to also be able to run on its own, by starting
    main().



I cannot start multiple VMS and the two apps have no means of communication.

Problem with this is:

Application.launch() can only be executed once per JVM.

The standard javaFx way to start the app:

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


Here it is impossible to fullwill Requirement 1). As both main() methods call launch().

A second approach i found is:

Updater + MainApp
public static void main(String[] args) {
Application app2 = JavaFxMainApp.class.newInstance();
Stage anotherStage = new Stage();
app2.start(anotherStage);
}


First off, i lose the possibility to pass args, but i can live with that as they would not be used anyways.
Major Problem here is, that this code ONLY works, if the JVM already has a JavaFx Thread running, hence it requires that launch() has been called in the JVM at some point before. This is not the case as none of the two calls launch() anymore.

Hybrid approach:

Updater calls launch(), MainApp takes the second approach


Requirement 2) cannot be fulfilled, als starting MainApp without launcher Updater is impossible now.

Another idea i had where dirty "try launch() catch()-> try second approach() in both apps, but that couples both apps and make the setup less flexible.

Is there a way to accomplish this without having to override JavaFxs' LauncherImpl or Application classes to fit these needs?

Answer

Can you do something like this:

public class MainApp extends Application {

    private Parent uiContent ;

    public static final double DEFAULT_WIDTH = 800 ;
    public static final double DEFAULT_HEIGHT = 600 ;

    public Parent getContent() {
        if (uiContent == null) {
            uiContent = initializeUI();
        }
        return uiContent ;
    }

    public Scene createScene() {
        return new Scene(getContent(), DEFAULT_WIDTH, DEFAULT_HEIGHT);
    }

    public void initializeAndShowStage(Stage stage) {
        stage.setScene(createScene());
        stage.show();
    }

    private Parent initializeUI() {
        // probably wise to check we are on the FX Application thread here...
        Pane root = ... ;
        // build ui....
        return root ;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        initializeAndShowStage(primaryStage);
    }

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

And

public class UpdaterApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        // whatever you need to launch the updater app here...

    }

    // invoke from the FX Application Thread to "start" main app:
    private void showMainApp(Stage stage) {
        MainApp app = new MainApp();
        app.initializeAndShowStage(stage);
    }

    private void showMainApp() {
        showMainApp(new Stage());
    }

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

This would be the far preferred approach. If you have requirements that force you to call main, then you could try something like this:

public class MainApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        // .... build UI etc
    }

    public static void main(String[] args) throws Exception {
        if (Platform.isFXApplicationThread()) {
            Stage someStage = new Stage();
            MainApp app = new MainApp();
            app.start(stage);
        } else {
            launch(args);
        }
    }
}

Then your updater app can just call MainApp().main(new String[0])); from the FX Application Thread.

This feels like a bit of a hack though.