看看这个例子:
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
的实际取消。是不是?
答案 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()
方法),并在回调状态下执行您想要执行的任何操作。