In my java program i give some options to the user and one of thems calls a JavaFXProgram to display something. I only want to run more code in the Java program when this JavaFX that got called actually exits, it may take 5 seconds, it may take a minute. Ideally what i would like is something like we have in Android. We call startActivityForResult()
and then wait for the call of onActivityResult()
.
How can i achieve similar behaviour in my situation?
I have this code that i wrote to try to replicate the problem i has having. It's similar idea but somehow this calls JavaFX, goes to start of loop and retrieves input from the user without a problem. In my other program i always get Exception in thread "main" java.util.InputMismatchException
when it goes back again to scan for input. But as i said, ideally, i would like to only run more code after JavaFX Application closes.
package JavaCallsJavaFXandWaits;
import java.util.Scanner;
import javafx.application.Application;
public class MyJavaProgram {
public static void main(String[] args) {
int input;
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("0 - exit");
System.out.println("1 - display something to me");
input = scanner.nextInt();
switch (input) {
case 0:
break;
case 1:
Application.launch(JavaCallsJavaFXandWaits.MyJavaFXProgram.class, null);
// how to get notified of MyJavaFXProgram exit? I only want to run code after it exits
break;
}
if (input == 0) break;
}
}
}
package JavaCallsJavaFXandWaits;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MyJavaFXProgram extends Application {
@Override
public void start(Stage primaryStage) {
Text oText = new Text("My JavaFXProgram");
StackPane root = new StackPane();
root.getChildren().add(oText);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
edit1:
I just noticed that if i try to display something two times (eg: choose 1, close JavaFX application, then choose 1 again) it crashes with Exception in thread "main" java.lang.IllegalStateException: Application launch must not be called more than once
. It seems that JavaFX Application is not exiting properly in this code too.
Your code doesn't work as you have it, because it doesn't fit in the JavaFX Application life-cycle, which is fully documented in the API documentation for
Application
. In brief, theApplication
class represents the entire application, (or perhaps the lifecycle of the application).To show a window in JavaFX, you must do so on the FX application thread, and the FX toolkit must be started in order to start this thread (among other things). The
Application.launch()
method starts the FX toolkit, starts the FX application thread, creates an instance of your application class, callsinit()
on that instance, and then callsstart()
on that instance (the call tostart()
happens on the FX Application Thread).As documented,
Application.launch()
blocks (does not return) until the FX toolkit shuts down (i.e. the application exits), and must be called only once. (Since it represents the entire application, this makes sense, and there is no way to work around calling it twice.)Your application structure doesn't really make any sense from a user perspective either. Why ask your user to interact with the command line to present options to a GUI-based application? You should present those options in the GUI. Just show a window with the options at startup, and then show a window corresponding to the option chosen. If you want to ensure that the user cannot return to the original window before the chosen option is complete, simply make the new window modal.
For example, if you refactor
MyJavaFXProgram
so it is not anApplication
subclass (which you should do, since it is not the starting point of the application):then you can do
If you really want to drive all this from the command line (and I honestly can't see a valid reason to do that), then it gets tricky and you necessarily have to get your hands dirty with managing interactions between threads at some point. Probably the simplest approach is to make sure the FX toolkit starts up when your application starts, and then to proceed more or less as you have in your code, but with
MyJavaFXProgram
refactored again so that it is not a subclass ofApplication
. There is a hack to start the FX toolkit, by creating aJFXPanel
, but that really is a bit of a hack and I prefer to do it explicitly by having anApplication
class that doesn't actually do anything, callinglaunch()
, and waiting for its initialization to complete. I showed how to do this in an answer to this question, so I'll just borrow that solution from there. Here is theApplication
class that is used to start the FX toolkit (but is not the main class that you execute):The idea is to call
Application.launch(FXStarter.class)
, but sincelaunch()
blocks, you need to do that on a background thread. Since that means your ensuing code might (probably will) execute beforelaunch()
has actually completed the work you need it to complete, you need to wait until it's done its job, which you can do withFXStarter.awaitFXToolkit()
. Then you can execute your loop. The only remaining thing to worry about is to make sure that you create and show new windows on the FX Application Thread. So yourMyJavaProgram
now looks like:(This uses the same version of
MyJavaFXProgram
above.)