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,除非您别无选择,否则我不推荐这样做。
在我的 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,除非您别无选择,否则我不推荐这样做。