billdoor
billdoor

Reputation: 2043

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?

Upvotes: 1

Views: 3613

Answers (2)

Pramod Kumar Sharma
Pramod Kumar Sharma

Reputation: 609

You want your MainApp to start of its own and you also want an UpdateApp to start the MainApp when required then you can follow my step. I have tried and this model is working.

This is the starting point. You need to call this Class to begin your application

public class StartAnApp {
    public static void main(String[] args){
        new Thread(new MainApp()).start(); // this will call your MainApp 
    }
}

This is the First Application which will start. As to start a JavaFX Application you need to have a main() methods. So ensure to provide a main method in this class.

public class MainApp extends Application implements Runnable{
    public MainApp(){}  // constructor

    @Override
    public void start(Stage stage){
        Text text = new Text("MainApp");
        Button startUpdate = new Button("Start Update");

        // When this button is pressed. It will launch UpdateApp Application
        startUpdate.setOnAction( e -> {
            Platform.runLater(new Runnable(){
                @Override
                public void run(){
                    new UpdateApp().start(new Stage());
                }
            });
        });

        Group root = new Group(text);
        Scene scene = new Scene(root,300,400);
        stage.setScene(scene);
        stage.setX(0);
        stage.setY(0);
        stage.show();
    }

    // This method will be used when you first start an Application for
    // which main method is required
    public static void main(String[] args){
        launch(args);
    }

    // This will be used when you call this Application from another JavaFX application
    // if you have closed this application before
    @Override
    public void run(){
        launch();
    }
}

This is your UpdateApp. This method does not have main() method.

public class UpdateApp extends Application implements Runnable{
    public UpdateApp(){}  // constructor

    @Override
    public void start(Stage stage){
        Text text = new Text("UpdateApp");
        Button startAnother = new Button("Start Another");

        // When this button is pressed. It will launch MainApp Application or you can add any other JavaApplication
        startAnother.setOnAction( e -> {
            Platform.runLater(new Runnable(){
                @Override
                public void run(){
                    new MainApp().start(new Stage());
                }
            });
        });

        Group root = new Group(text);
        Scene scene = new Scene(root,300,400);
        stage.setScene(scene);
        stage.setX(350);
        stage.setY(0);
        stage.show();
    }

    // This will be used when you call this Application from another JavaFX application
    @Override
    public void run(){
        launch();
    }
}

Upvotes: 3

James_D
James_D

Reputation: 209330

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.

Upvotes: 3

Related Questions