在我的JavaFX应用程序中,我想创建一个全局未捕获的异常处理程序,它创建一个对话框窗口,显示错误消息和异常的堆栈跟踪,然后终止。我使用Thread.setDefaultUncaughtExceptionHandler()
并在那里创建对话框,但异常处理程序本身的代码在调用它时会不断抛出异常。
原因是当JavaFX应用程序线程中抛出任何未捕获的异常时,JVM会终止该线程。并且JavaFX线程必须运行才能创建JavaFX组件 - 因此无法在异常处理程序中创建这样的对话框...所以我的问题是这样做的最佳方法是什么?我想在发生未捕获的异常时向用户显示一条消息,而不是让应用程序突然终止。
这就是我一直在使用的:
public void start(Stage primaryStage) throws IOException {
Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e) -> {
Dialogs.create().title("Error")
.message("An uncaught exception was thrown in thread \"" + t
+ "\". Click below to view the stacktrace, or close this "
+ "dialog to terminate the application.")
.style(DialogStyle.NATIVE)
.showExceptionInNewWindow(e);
Platform.exit();
});
primaryStage.show();
method();
}
public void method() throws IOException {
//Performs file i/o operations and throws IOException if an error occurs
throw new IOException();
}
程序后期也可能会抛出更多异常。 (顺便说一句,我使用ControlsFX库来创建对话框窗口。)这是我在运行代码时得到的错误消息:
Exception in Application start method
Exception: java.lang.IllegalStateException thrown from the UncaughtExceptionHandler in thread "main"
答案 0 :(得分:3)
我认为这里发生的事情是start()
方法抛出的异常被调用该方法的代码捕获。因此,start()
方法中抛出的任何内容都不会由您的异常处理程序处理(它不是未捕获的异常)。
但是,start()
方法完成后抛出的异常由异常处理程序处理。
但请注意,异常将在抛出它们的线程中处理。因此,只要在FX应用程序线程上抛出这些异常,您的代码就会起作用,但不适用于后台线程中抛出的异常(因为您无法在后台线程中创建并显示对话框)。
涵盖所有可能性:
start()
方法中抛出异常或错误,请将该代码放在try
- catch
块中,并按常规方式处理Platform.runLater()
显示对话框。这将处理FX应用程序线程之外引发的异常。示例(start()
方法中出现错误,概率为50%):
import java.io.IOException;
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.controlsfx.dialog.Dialogs;
import org.controlsfx.dialog.DialogStyle;
public class UncaughtExceptionHandlerExample extends Application {
@Override
public void start(Stage primaryStage) {
Thread.setDefaultUncaughtExceptionHandler((t, e) -> Platform.runLater(() -> showErrorDialog(t, e)));
Thread.currentThread().setUncaughtExceptionHandler(this::showErrorDialog);
try {
Button button = new Button("Create error");
button.setOnAction(event -> {
throw new Error("Boom!");
});
Button backgroundErrorButton = new Button("Create error in background thread");
backgroundErrorButton.setOnAction(event -> {
new Thread(() -> {
throw new Error("Boom");
}).start();
});
HBox root = new HBox(5, button, backgroundErrorButton);
Scene scene = new Scene(root, 400, 80);
primaryStage.setScene(scene);
primaryStage.show();
if (new Random().nextDouble() < 0.5) {
throw new Error("Boom");
}
} catch (Throwable t) {
showErrorDialog(Thread.currentThread(), t);
}
}
private void showErrorDialog(Thread t, Throwable e) {
Dialogs.create().title("Error")
.message("An uncaught exception was thrown in thread " + t
+ ". Click below to view the stacktrace, or close this "
+ "dialog to terminate the application.")
.style(DialogStyle.NATIVE)
.showExceptionInNewWindow(e);
Platform.exit();
}
public static void main(String[] args) {
launch(args);
}
}
顺便说一下,如果发生未捕获的异常,JVM会终止FX应用程序线程,这是不对的。
答案 1 :(得分:0)
我建议您在发生异常时使用platform.runLater()
方法在JavaFx主线程中创建对话框组件。