3

In a JavaFX application, I have a method which takes a long time on large input. I'm opening a dialog when it is loading and I'd like the user to be able to cancel/close out the dialog and the task will quit. I created a task and added its cancellation in the cancel button handling. But the cancellation doesn't happen, the task doesn't stop executing.

Task<Void> task = new Task<Void>() {
    @Override
    public Void call() throws Exception {
        // calling a function that does heavy calculations in another class
    };
    task.setOnSucceeded(e -> {
        startButton.setDisable(false);
    });
}
new Thread(task).start();

cancelButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
    System.out.println("Button handled");
    task.cancel();
}
);

Why isn't the task getting canceled when the button clicked?

AbdelKh
  • 499
  • 7
  • 19
  • 2
    You must handle cancelling the task inside the implementation. Please read the last paragraph before the examples in [the documentation](https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html): "The user of the Task will request that it be cancelled, and the author of the Task must check whether is has been cancelled within the body of the call method. " – Itai May 24 '17 at 10:18
  • @sillyfly Yes that was useful. But how can I check task's cancellation if the heavy part is done in a function in another class? – AbdelKh May 24 '17 at 10:35
  • 1
    That depends quite a lot on the implementation of that other class. There is no "catch-all" solution, but generally you either have a task made up of a lot of smaller "steps", so you can check a flag in each iteration, or you have some form of `wait` which will respond to an `interrupt`. – Itai May 24 '17 at 11:29
  • 2
    [This article](https://www.ibm.com/developerworks/library/j-jtp05236/) is quite a useful (despite being old) explanation of interruption of threads, and may provide a way to do what you need. – James_D May 24 '17 at 12:16
  • @sillyfly my implementation actually is a JAR file method that I call and obtain results from. Is there any way to cancel the task in this case? – AbdelKh May 27 '17 at 15:42

1 Answers1

4

You have to check on the cancel state (see Task's Javadoc). Have a look at this MCVE:

public class Example extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                new AnotherClass().doHeavyCalculations(this);
                return null;
            }
        };

        Button start = new Button("Start");
        start.setOnMouseClicked(event -> new Thread(task).start());

        Button cancel = new Button("Cancel");
        cancel.setOnMouseClicked(event -> task.cancel());

        primaryStage.setScene(new Scene(new HBox(start, cancel)));
        primaryStage.show();
    }

    private class AnotherClass {

        public void doHeavyCalculations(Task<Void> task) {
            while (true) {
                if (task.isCancelled()) {
                    System.out.println("Canceling...");
                    break;
                } else {
                    System.out.println("Working...");
                }
            }
        }

    }

}

Note that…

  • You should use Task#updateMessage(String) rather than printing to System.out, here it's just for demonstration.
  • Directly injecting the Task object creates a cyclic dependency. However, you can use a proxy or something else that fits your situation.
beatngu13
  • 7,201
  • 6
  • 37
  • 66
  • What if the heavy task is done by a function in another class? And doesn't while true mean that the execution will never end? – AbdelKh May 24 '17 at 10:26
  • 1
    @Joker00 make that class be aware of the cancel state. For instance, inject the `Task` object as method parameter. See edit... – beatngu13 May 24 '17 at 10:45
  • This is useful and I am marking your answer a resolving one but I still have one issue, the heavy task is sometimes done by a JAR file function, this is simply called and left to work. Its code isn't visible nor editable and thus, I can't check task's cancellation the way you explained in your answer. Is there any other way around this case? – AbdelKh May 27 '17 at 15:39
  • @Joker00 Glad that did help. Regarding your follow-up question: You may want to have a look at [How do you kill a thread in Java?](https://stackoverflow.com/q/671049/3429133) – beatngu13 May 27 '17 at 23:38