Reputation: 2043
following Scenario:
JavaFxMainApp JavaFXUpdaterApp
Both are JavaFX applications with a GUI and a void main() method.
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().
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
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
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