在我的JavaFX应用程序中,我想显示一个错误对话框,并在发生意外异常时退出应用程序。所以在我的main方法中,我在启动应用程序之前设置了一个默认的未捕获异常处理程序:
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
在内部推送runnable,但是等待runnable的执行(带有一些倒计时锁存机制)。
现在问题是:当我的start()方法发生异常时,我的应用程序卡住了。因为它试图等到执行对话框显示,但FXAT从未执行此操作。我想这是因为当start()方法因异常而失败时,FXAT刚刚死了?我不确定这是否是start()方法的特殊情况,或者在抛出异常并且未在FXAT执行的代码中捕获时是否会发生这种情况。
在Swing中我知道EDT是一个由多个线程组成的复杂架构。情况并非如此,当EDT上的某些执行失败时,整个Swing都会崩溃。但这里似乎发生了什么?
那我该怎么办?如何向用户显示应用程序无法启动?
答案 0 :(得分:1)
渴求
我有一个解决方案,但我不特别推荐它。默认情况下,Application.launch()
将捕获start方法抛出的异常,退出FX平台,然后重新抛出异常。由于FX应用程序线程在您的默认未捕获异常处理程序执行时已关闭,因此等待FX应用程序线程上发生的事情只会无限期地阻塞。
例外情况是FX应用程序在Web启动时运行。启动程序检查此方法的方法是检查是否存在安全管理器。所以一个(真的,非常丑陋)的解决方法是安装一个安全管理器,使它看起来像你在web启动模式下运行。这一行将安装一个宽松的安全管理器:
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");
}
}
正如我所说,这确实是一个很大的问题,除非你别无选择,否则我不会真的推荐这个。