JavaFX 8中的常规异常处理

给定一个Scene的控制器调用业务代码,该代码引发异常.我怎样才能以一般方式处理这种异常?

我尝试了Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)方法但是没有调用它,所以我相信异常会被捕获到JavaFX框架内的某个地方.

我该怎么做才能处理此异常或至少向用户显示一些有用的信息?

从JavaFX 8开始,Thread.setDefaultUncaughtExceptionHandler(…)应该可以工作:参见RT-15332.

如果在执行start(…)方法期间发生未捕获的异常,则事情会有点复杂.根据应用程序的启动方式,调用start()的代码(例如Application.launch(…)的实现)可能会捕获异常并处理它,这显然会阻止调用默认的异常处理程序.

特别是在我的系统上(Mac OS X 10.9.5上的JDK 1.8.0_20),似乎我的应用程序通过调用Application.launch(…)的main(…)方法启动,任何异常抛出start()方法被捕获(而不是重新抛出).

但是,如果我删除main(…)方法(请参阅下面的注释)并直接启动应用程序,则会重新抛出start()方法中抛出的任何异常,从而允许调用默认的异常处理程序.请注意,它不仅仅是向上传播.在FX应用程序线程上调用start(),并从主线程重新抛出异常.实际上,当发生这种情况时,假定FX应用程序线程正在运行的默认处理程序中的代码无法运行:所以我的猜测是在这种情况下启动代码捕获start()方法中的异常,并且在catch块中关闭在FX应用程序线程中,然后从调用线程重新抛出异常.

所有这一切的结果是重要的是 – 如果您希望默认处理程序在start()方法中处理异常,如果FX应用程序线程上没有抛出异常(即使通过平台),也不应该调用任何UI代码.runLater(…)).

注意:(对于那些可能不知道这一点的人).从Java 8开始,即使没有main(…)方法,也可以直接启动Application子类,方法是以通常的方式将类名作为参数传递给JVM可执行文件(即java MyApp).这符合您的期望:启动FX工具包,启动FX Application线程,实例化Application子类并调用init(),然后在FX Application Thread调用start().有趣的是(也许是错误的),调用Application.launch()的main(…)方法在start(…)方法中对未捕获的异常的行为略有不同.

这是一个基本的例子.取消注释Controller.initialize()中的代码,以查看start()方法中抛出的异常.

package application;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {

        Thread.setDefaultUncaughtExceptionHandler(Main::showError);

        Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
        Scene scene = new Scene(root,400,400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private static void showError(Thread t, Throwable e) {
        System.err.println("***Default exception handler***");
        if (Platform.isFxApplicationThread()) {
            showErrorDialog(e);
        } else {
            System.err.println("An unexpected error occurred in "+t);

        }
    }

    private static void showErrorDialog(Throwable e) {
        StringWriter errorMsg = new StringWriter();
        e.printStackTrace(new PrintWriter(errorMsg));
        Stage dialog = new Stage();
        dialog.initModality(Modality.APPLICATION_MODAL);
        FXMLLoader loader = new FXMLLoader(Main.class.getResource("Error.fxml"));
        try {
            Parent root = loader.load();
            ((ErrorController)loader.getController()).setErrorText(errorMsg.toString());
            dialog.setScene(new Scene(root, 250, 400));
            dialog.show();
        } catch (IOException exc) {
            exc.printStackTrace();
        }
    }

//  public static void main(String[] args) {
//      launch(args);
//  }
}

使用Main.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.geometry.Insets?>

<HBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller"
    alignment="center" spacing="5">
    <children>
        <Button text="Do something safe" onAction="#safeHandler" />
        <Button text="Do something risky" onAction="#riskyHandler" />
        <Label fx:id="label" />
    </children>
    <padding>
        <Insets top="10" left="10" right="10" bottom="10" />
    </padding>
</HBox>

Controller.java:

package application;

import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class Controller {
    private final IntegerProperty counter = new SimpleIntegerProperty(1);

    @FXML
    private Label label ;

    public void initialize() throws Exception {
        label.textProperty().bind(Bindings.format("Count: %s", counter));

        // uncomment the next line to demo exceptions in the start() method:
        // throw new Exception("Initializer exception");
    }

    @FXML
    private void safeHandler() {
        counter.set(counter.get()+1);
    }

    @FXML
    private void riskyHandler() throws Exception {
        if (Math.random() < 0.5) {
            throw new RuntimeException("An unknown error occurred");
        }
        safeHandler();
    }
}

Error.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.ErrorController">
    <center>
        <ScrollPane>
            <content>
                <Label fx:id="errorMessage" wrapText="true" />
            </content>
        </ScrollPane>
    </center>
    <bottom>
        <HBox alignment="CENTER">
            <Button text="OK" onAction="#close"/>
        </HBox>
    </bottom>
</BorderPane>

ErrorController.java:

package application;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class ErrorController {
    @FXML
    private Label errorMessage ;

    public void setErrorText(String text) {
        errorMessage.setText(text);
    }

    @FXML
    private void close() {
        errorMessage.getScene().getWindow().hide();
    }
}
https://stackoverflow.com/questions/26361559/general-exception-handling-in-javafx-8

转载注明原文:JavaFX 8中的常规异常处理