Java прослушивает JavaFX Thread [дубликат]

13
задан Hannes 14 October 2014 в 14:04
поделиться

2 ответа

Как и в случае с 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:

<?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();
    }
}

Ошибка.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();
    }
}
24
ответ дан James_D 25 August 2018 в 20:47
поделиться

На самом деле это довольно сложно, я столкнулся с той же проблемой раньше, и я не мог придумать какие-либо изящные решения. Очевидно, что один действительно тяжелый способ (и, честно говоря, возможно, совершенно неправильный способ) справиться с этим, заключается в каждом из методов класса контроллера (те, которые начинаются с @FXML), обертывают весь кусок метода в try{} catch(Throwable t){}, а затем внутри вашего захватывающего улова, сделайте некоторый анализ результата исключения, чтобы попытаться определить, какую полезную информацию показывать пользователю в случае катастрофы.

Также стоит отметить, что по крайней мере в Javafx 8 (я не пробовал с 2.0-2.2), если вы попытаетесь обернуть место, где вы загружаете FXML (например, в ваш основной метод приложений «Старт» , например), в том же самом блоке throwable, он не получает исключения из класса Controller, который, по-видимому, подразумевает какое-то разделение между этим потоком и тем, которое используется в классе контроллера FXML. Тем не менее, он определенно относится к одному и тому же потоку приложения, поскольку, если вы сохраняете ссылку на объект Thread.currentThread(); в вызывающем классе, а затем выполняете то же самое в контроллере, .equals на двух будет true. Таким образом, под листами Джавафкс делает магию, чтобы отделить неконтролируемые исключения от этих классов.

Я не смотрел дальше, чем это.

По правде говоря, я ненавижу даже получить этот ответ здесь, потому что я боюсь, что кто-то будет использовать его без правильного понимания того, насколько это неверно. Таким образом, если кто-то запишет лучший ответ, я немедленно удалю это.

Удачи!

0
ответ дан WillBD 25 August 2018 в 20:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: