如何等待取消服务后的任务#取消?

时间:2015-03-28 20:16:41

标签: javafx javafx-8

看看这个例子:

package sample;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.stage.Stage;

public class Main extends Application {

    //NOTICE: This is class in **other file** (here is just for example)
    private static class MyService extends Service {
        @Override
        protected Task createTask() {
            return new Task() {
                @Override
                protected Object call() throws Exception {
                    System.out.println("Service: START");

                    while(true) {
                        System.out.println("Service: ITERATION");

                        // Thread.sleep(3000); // This raise InterruptedException after cancel, but how about such code (it won't raise exception):

                        for(long i = 0; i < 1_000_000_000; i++) {
                        }

                        if (isCancelled())
                            break;
                    }

                    System.out.println("Service: END");

                    return null;
                }
            };
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        MyService myService = new MyService();
        myService.start();

        Thread.sleep(5000);

        myService.cancel();

        System.out.println(myService.getState()); // Here is `CANCELLED` already but task isn't finished yet.

        // <--- How to wait cancellation of Task here?

        System.out.println("This command must be called after `Service: END`");

        Platform.exit();
    }

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

如您所知,Service#cancel的来电不会等同取消Task。所以,我想阻止主线程并等待取消Task。我该怎么办?

P.S。

看起来Service没有提供任何回调/事件处理程序来检查Task的实际取消。是不是?

2 个答案:

答案 0 :(得分:2)

永远不要阻止FX应用程序线程。

Service类确实定义了setOnCancelled(...)方法,用于注册回调:

myService.setOnCancelled(event -> {
    System.out.println("Service was cancelled");
});

请注意,取消Service时,如果线程被阻止,它将中断该线程。因此,如果您没有抓住InterruptedException,它就不会正常退出call方法。这就是为什么你没有看到&#34; END&#34;消息。

完整的示例代码:

import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ServiceCancellationTest extends Application {

    //NOTICE: This is class in **other file** (here is just for example)
    private static class MyService extends Service<Void> {
        @Override
        protected Task<Void> createTask() {
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    System.out.println("Service: START");

                    while(! isCancelled()) {
                        System.out.println("Service: ITERATION");
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException interrupted) {
                            System.out.println("Task interrupted");
                        }

                        if (isCancelled())
                            break;
                    }

                    System.out.println("Service: END");

                    return null;
                }
            };
        }
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        MyService myService = new MyService();
        myService.start();

        myService.setOnCancelled(event -> {
            System.out.println("In cancelled callback: "+myService.getState()); // Here is `CANCELLED` already but task isn't finished yet.

        });

        // You should never block the FX Application Thread. To effect a pause,
        // use a pause transition and execute the code you want in its 
        // onFinished handler:

        PauseTransition pause = new PauseTransition(Duration.seconds(5));
        pause.setOnFinished(event -> {
            myService.cancel();
            System.out.println("After calling cancel: "+myService.getState());
            System.out.println("This command must be called after `Service: END`");

            Platform.exit();
        });
        pause.play();


    }

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

答案 1 :(得分:2)

默认情况下,Service.cancel()会中断任务。因此必须提出InterruptedException,并且(强制)终止您的任务。

您可以做的一件事是将创建的任务存储在MyService类的全局变量中,并覆盖取消方法,如下所示:

class MyService extends Service {

    private Task t;

    @Override
    public boolean cancel() {
        if (t != null) {
            return t.cancel(false);
        } else {
            return false;
        }
    }

    @Override
    protected Task createTask() {
        t = new Task() { /* ... */ };
        return t;
    }
}

其余的都很简单。在服务状态属性中添加一个更改侦听器(或使用setOnCanceled()方法),并在回调状态下执行您想要执行的任何操作。