为什么javafx应用程序无法启动Platform.runLater以及为什么要挂起lambda表达式?

时间:2017-05-08 16:44:22

标签: java multithreading javafx lambda

我有简单的javafx HelloWord应用程序,但是当我尝试使用Platform.runLater正确启动时,我会收到java.lang.RuntimeException。类似地,当我尝试使用lambda表达式时,我的框架不会显示出来。程序打印'开始',但挂起显示框架。

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();
    }
}

这是应用程序产生的整个输出:

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

有人可以向我解释为什么我会收到异常以及为什么lambda表达式会挂起应用程序吗?

1 个答案:

答案 0 :(得分:1)

Application.launch(args)相当于Application.launch(TheClass.class, args),其中TheClass是方法调用的直接包含类TheClass必须是Application的子类,否则会引发IllegalArgumentException。第一个代码块中的直接封闭类是匿名内部Runnable子类,它本身不是Application的子类,因此您获得IllegalArgumentException

您认为启动JavaFX应用程序的正确方法是从FX应用程序线程调用launch()是不正确的。事实上,FX应用程序线程在FX工具包启动之前不会运行,并且在调用launch()之前不会发生。因此,您的第二个代码块无法执行任何操作:没有FX Application Thread可以执行launch()。此外,launch()方法会阻止应用程序退出(再次参见documentation);因此,即使FX应用程序线程正在运行(*),您也只是将其锁定,因为它将被阻止,等待它自己的退出。

"适当"启动JavaFX应用程序的方法只是从主线程(或任何其他线程,而不是JavaFX应用程序线程)调用launch()

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();
    }
}

在此处调用launch()将启动FX工具包,启动FX应用程序线程并创建MainFrame的实例。然后,在FX应用程序线程上,它将创建一个Stage并将其传递给MainFrame实例的start()方法。

(*)我认为您拥有代码的方式,第一次尝试调用launch()失败将实际启动FX Toolkit,因此第二次尝试会在FX应用程序线程上调用launch(),导致死锁,如上所述。