JavaFX:启动方法失败时如何显示错误对话框?

JavaFX: How to show error dialog when start method fails?

在我的 JavaFX 应用程序中,我想显示错误对话框并在出现意外异常时退出应用程序。所以在我的主要方法中,我在启动应用程序之前设置了一个默认的未捕获异常处理程序:

setDefaultUncaughtExceptionHandler((thread, cause) -> {
    try {
        cause.printStackTrace();
        final Runnable showDialog = () -> {
           // create dialog and show
        };
        if (Platform.isFxApplicationThread()) {
           showDialog.run();
        } else {
           runAndWait(showDialog);
        }
    } catch (Throwable t) {
        // ???
    } finally {
        System.exit(-1);
    }
});

launch(MyApp.class);

说明:当未捕获的异常处理程序在 JavaFX 应用程序线程 (FXAT) 上执行时,我只是 运行 显示对话框的代码。当 FXAT 未调用异常处理程序时,这当然不起作用。在这种情况下,我必须将代码推送到 FXAT。但是我不能使用 Platform.runLater 因为那样我的应用程序会在显示对话框之前退出。因此,我创建了自定义方法 runAndWait,它在内部通过 Platform.runLater 推送 运行nable,但会等到 运行nable 执行(使用一些倒计时闩锁机制)。

现在的问题是:当我的 start() 方法发生异常时,我的应用程序就会卡住。因为它试图等到对话框显示的执行,但 FXAT 从不执行此操作。我猜这是因为当 start() 方法因异常而失败时,FXAT 就死了?我不确定这是否是 start() 方法的特例,或者在抛出异常但未在 FXAT 执行的代码中捕获的任何情况下是否会发生这种情况。

据我所知,在 Swing 中,EDT 是一个由多个线程组成的复杂架构。当 EDT 上的某些执行失败时,并不是整个 Swing 都崩溃了。但这里似乎发生了什么?

那我能在这里做什么呢?如何向用户显示应用程序无法启动?

嗯....

我有一个解决方案,但我并不特别推荐。默认情况下,Application.launch()会捕获start方法抛出的异常,退出FX平台,然后重新抛出异常。由于 FX 应用程序线程在默认的未捕获异常处理程序执行时已关闭,等待 FX 应用程序线程上发生的事情只会无限期地阻塞。

Web 启动中的 FX 应用程序 运行 除外。启动器对此进行检查的方式是检查是否存在安全管理器。所以一个(真的,真的很难看)解决方法是安装一个安全管理器,这样看起来你 运行 处于网络启动模式。此行将安装一个许可的安全管理器:

System.setSecurityManager(new SecurityManager(){
    @Override
    public void checkPermission(Permission perm) {}
});

这是一个 SSCCE:

import java.lang.Thread.UncaughtExceptionHandler;
import java.security.Permission;
import java.util.concurrent.FutureTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;

public class ShowDialogOnException  {

    public static final UncaughtExceptionHandler ALERT_EXCEPTION_HANDLER = (thread, cause) -> {
        try {
            cause.printStackTrace();
            final Runnable showDialog = () -> {
               Alert alert = new Alert(AlertType.ERROR);
               alert.setContentText("An unknown error occurred");
               alert.showAndWait();
            };
            if (Platform.isFxApplicationThread()) {
               showDialog.run();
            } else {
               FutureTask<Void> showDialogTask = new FutureTask<Void>(showDialog, null);
               Platform.runLater(showDialogTask);
               showDialogTask.get();
            }
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            System.exit(-1);
        }
    };



    public static void main(String[] args) {
        System.setSecurityManager(new SecurityManager(){
            @Override
            public void checkPermission(Permission perm) {}
        });
        Thread.setDefaultUncaughtExceptionHandler(ALERT_EXCEPTION_HANDLER);
        Application.launch(App.class, args);
    }
}

和一个测试应用程序:

import javafx.application.Application;
import javafx.stage.Stage;

public class App extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        throw new Exception("An exception");
    }

    @Override
    public void stop() {
        System.out.println("Stop");
    }

}

正如我所说,这确实是一个大 hack,除非您别无选择,否则我不推荐这样做。