2015-03-28 3 views
0

Посмотрите на этот пример:Как отменить отмену задачи после отмены услуги # отменить?

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

Никогда не блокируйте резьбу приложения FX.

Service класс действительно задает setOnCancelled(...) метод, который используется для регистрации обратного вызова:

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

Обратите внимание, что при отмене Service, он будет прерывать поток, если он заблокирован. Поэтому, если вы не поймаете InterruptedException, он не будет нормально выходить из метода call. Вот почему вы не видите сообщение «END».

Полный пример кода:

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); 
    } 
} 
+0

Проверьте свое решение в моем примере. Вы увидите, что этот обратный вызов вызывается после 'cancel', но задача по-прежнему не отменяется/не завершается. Итак, я хочу найти подходящее решение для ожидания отмены задачи. Постскриптум Я знал, какой блок основного потока - плохая идея, но в моем случае это может быть подходящее решение. –

+0

Работает отлично для меня ... Я обновлю полный код. –

+0

Ваше решение не печатает 'Сервис: END'. Это означает, что Задача не отменена/завершена, но состояние службы уже установлено CANCELED. Думаю, теперь вы понимаете проблему. –

2

По умолчанию Service.cancel() прерывает Task. Таким образом, должен быть поднят 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()) и выполняйте то, что вы хотите сделать после изменения состояния в обратном вызове.