Reputation: 841
I have simple javafx HelloWord application, but when i try to start it properly using Platform.runLater i receive java.lang.RuntimeException. Similarly when i try to use lambda expression, my frame doesn't show up. Program prints 'starting', but hangs up on showing the frame.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class MainFrame extends Application {
public static void main(String[] args) {
Platform.runLater(new Runnable() {
@Override
public void run() {
MainFrame.launch(args);
}
});
Platform.runLater(() -> {
System.out.println("starting");
launch(args);
System.out.println("started");
});
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction((e)->System.out.println("Hello World!"));
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
This is entire output produced by the application:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException:
Error: class MainFrame$1 is not a subclass of javafx.application.Application
at javafx.application.Application.launch(Unknown Source)
at MainFrame$1.run(MainFrame.java:15)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(Unknown Source)
at java.lang.Thread.run(Unknown Source)
starting
Could anyone explain to me why i receive exception and why lambda expression hangs up application?
Upvotes: 2
Views: 1524
Reputation: 209388
Application.launch(args)
is equivalent to Application.launch(TheClass.class, args)
, where TheClass
is the immediately enclosing class of the method call. TheClass
must be a subclass of Application
, else an IllegalArgumentException
is thrown. The immediately enclosing class in your first code block is the anonymous inner Runnable
subclass that is not itself a subclass of Application
, so you get the IllegalArgumentException
.
Your assumption that the proper way to start a JavaFX application is by calling launch()
from the FX Application Thread is incorrect. In fact, the FX Application Thread will not be running until the FX toolkit is started, and that will not happen until launch()
is called. Hence your second code block just doesn't do anything: there is no FX Application Thread on which to execute launch()
. In addition, the launch()
method blocks until the application exits (again, see documentation); so even if the FX Application Thread were running(*), you would simply deadlock it as it would be blocked, waiting for its own exit.
The "proper" way to start a JavaFX Application is simply to call launch()
from the main thread (or any other thread, but not the JavaFX Application Thread):
public class MainFrame extends Application {
public static void main(String[] args) {
System.out.println("Calling launch from main thread: "+Thread.currentThread());
launch(args);
}
@Override
public void start(Stage primaryStage) {
System.out.println("start() invoked on thread: "+Thread.currentThread());
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction((e)->System.out.println("Hello World!"));
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
Calling launch()
here will start the FX toolkit, start the FX Application Thread and create an instance of MainFrame
. Then, on the FX Application Thread, it will create a Stage
and pass it to the MainFrame
instance's start()
method.
(*) I think the way you have your code, the first failed attempt to call launch()
will actually start the FX Toolkit, so the second attempt does call launch()
on the FX Application Thread, causing deadlock as described.
Upvotes: 2