简体   繁体   中英

JavaFX's task seem to consume exceptions. Is it a bug or a feature?

Consider this code:

Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e) -> {
    System.out.println("An exception occurred!");
});

// set the exception handler for the JavaFX application thread
Thread.currentThread().setUncaughtExceptionHandler((Thread t, Throwable e) -> {
    System.out.println("An exception occurred!");
});

Task<?> task = new Task() {
    @Override
    public Void call() throws Exception {
        throw new RuntimeException("foobar");
    };
};

new Thread(task).start();

If we run the code the runtime exception never triggers the default exception handler but is instead consumed by the task. The only way to counteract this that I found is to rethrow the exception in task.setOnFailed:

task.setOnFailed((WorkerStateEvent t) -> {
    throw new RuntimeException(task.getException());
});

Since JavaFX 8 now has support for the UncaughtExceptionHandler why isn't the exception propagated to the exception handler?

Inside the Task.call() method just throw the exception and add a ChangeListener to the task like this:

task.exceptionProperty().addListener((observable, oldValue, newValue) ->  {
  if(newValue != null) {
    Exception ex = (Exception) newValue;
    ex.printStackTrace();
  }
});

Then, after the task failed with the exception, you get notified by the listener which exception was thrown during execution. You can easily exchange the line ex.printStackTrace(); with an Alert if you are in the JavaFX Execution Thread.

A bit late probably but you can print the throwable itself:

task.setOnFailed(new EventHandler<WorkerStateEvent>() {
    @Override
    public void handle(WorkerStateEvent arg0) {
        Throwable throwable = task.getException(); 
        throwable.printStackTrace();
    }
}

The exceptions gets thrown anyway, but you can use this to display it to the user or log it.

Feature, Task maintains an exception property itself. The idea is, that when an exception is thrown, the tasks fails, and one may ask which exception was thrown. In that respect Task was conceived as a quasi batch job, running in the background, and possibly silently failing.

That reflects also a bit of the asynchrone behaviour; where the exception could be handled. Not at the place where start was called.

I know that this question is old, but I searched for an answer and have not found very much about this topic. I think you can rethrow the exception if you like to do so. Here is the sample code:

public class Main extends Application {

@Override
public void start(Stage stage) {

    Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            throw new IndexOutOfBoundsException();
        }
    };
    task.setOnSucceeded(evt -> System.out.println("Task succeeded!"));
    task.setOnCancelled(evt -> System.out.println("Task cancelled!"));
    task.setOnFailed(evt -> {
        System.out.println("Task failed!");
        if (task.getException() instanceof IndexOutOfBoundsException) {
            System.out.println("...with an IndexOutOfBoundsException");
        } else if (task.getException() instanceof NumberFormatException) {
            System.out.println("...with a NumberFormatException");
        } else {
            System.out.println("...with another, unexpected execption");
        }
    });

    VBox box = new VBox();
    Scene scene = new Scene(box, 200, 200);
    stage.setScene(scene);
    stage.setTitle("Thread Example");
    stage.show();

    new Thread(task).start();

}

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

Console-Output: Task failed! ...with a IndexOutOfBoundsException

If an exception is thrown inside a task, then the task ends up in the state 'failed'. In the setOnFailed method you can handle the failure of the task. All code inside this method is on the application thread of JavaFX but you can rely on the exception of the task by task.getException(). Besides, this code only works with JavaFX (I tried to get the same output in a normal java app, but it did not work out).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM