如何创建使用JavaFX的默认异常处理程序?

时间:2014-08-05 18:33:20

标签: java javafx

在我的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"

2 个答案:

答案 0 :(得分:3)

认为这里发生的事情是start()方法抛出的异常被调用该方法的代码捕获。因此,start()方法中抛出的任何内容都不会由您的异常处理程序处理(它不是未捕获的异常)。

但是,start()方法完成后抛出的异常由异常处理程序处理。

但请注意,异常将在抛出它们的线程中处理。因此,只要在FX应用程序线程上抛出这些异常,您的代码就会起作用,但不适用于后台线程中抛出的异常(因为您无法在后台线程中创建并显示对话框)。

涵盖所有可能性:

  1. 如果可能在start()方法中抛出异常或错误,请将该代码放在try - catch块中,并按常规方式处理
  2. 设置默认未捕获的异常处理程序以调用代码以使用Platform.runLater()显示对话框。这将处理FX应用程序线程之外引发的异常。
  3. 在FX应用程序线程上设置一个未捕获的异常处理程序,以调用代码直接显示该对话框。
  4. 示例(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主线程中创建对话框组件。