Как и в случае с JavaFX 8, Thread.setDefaultUncaughtExceptionHandler(...)
должен работать: см. RT-15332 .
Вещи немного сложны, если во время выполнения start(...)
метод. В зависимости от того, как приложение запускается, код, вызывающий start()
(например, реализация Application.launch(...)
), может перехватывать исключение и обрабатывать его, что, очевидно, предотвращает вызов обработчика исключений по умолчанию.
В частности, в моей системе (JDK 1.8.0_20 на Mac OS X 10.9.5), похоже, что если мое приложение запускается с помощью метода main(...)
, который вызывает Application.launch(...)
, любое исключение, вызванное методом start()
поймано (и не возрождается).
Однако, если я удаляю метод main(...)
(см. примечание ниже) и запускаю приложение напрямую, любое исключение, возникшее в методе start()
, будет восстановлено, что позволит вызвать обработчик исключений по умолчанию. Обратите внимание, что он не просто распространяется. start()
вызывается в потоке приложения FX, и исключение возвращается из основного потока. В самом деле, когда это происходит, код в обработчике по умолчанию, который предполагает, что поток приложения FX работает, не запускается: поэтому я предполагаю, что код запуска в этом случае ловит исключения в методе start()
и в блоке catch
, выключает FX Application Thread
, а затем переводит исключение из вызывающего потока.
В результате все это важно: если вы хотите, чтобы ваш обработчик по умолчанию обрабатывал исключения в методе start()
, вы не должны вызывать какой-либо код пользовательского интерфейса, если исключение не выбрасывается на FX Тема приложения (даже через Platform.runLater(...)
).
Примечание: (для тех, кто может не знать об этом). Начиная с Java 8, вы можете напрямую запустить подкласс Application
, даже если у него нет метода main(...)
, передав имя класса в качестве аргумента в исполняемый файл JVM обычным способом (т. Е. java MyApp
). Это делает то, что вы ожидаете: запускает инструментарий FX, запускает поток приложения FX, создает подклассу Application
и вызывает init()
, а затем в приложении Application Application FX start()
. Интересно (и, возможно, неправильно), метод main(...)
, который вызывает Application.launch()
, ведет себя несколько иначе по отношению к неперехваченным исключениям в методе 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:
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();
}
}
Ошибка.fxml:
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();
}
}