2016-07-29 2 views
0

У меня есть служба JavaFX, в котором задача создается, чтобы сделать некоторую работу:Как получить экземпляр Task от метода обработчика службы в JavaFX

public class MyService extends Service<Void> { 
    @Override 
    protected Task<Void> createTask() { 
     return new Task<Void>() { 
      @Override 
      protected Void call() throws Exception { 
       // do some work 
       return null; 
      } 
     }; 
    } 
} 

У меня также есть обработчик для успеха:

myService.setOnSucceeded(new EventHandler<WorkerStateEvent>() { 
    @Override 
    public void handle(final WorkerStateEvent event) { 
     // check what Task has finished 
    } 
}); 

Как можно запустить службу более одного раза одновременно, я не смогу отличить от обработчика успеха, какой экземпляр Task завершился. Заглянув в документ, я не мог найти способ, как это сделать.

Моя идея состояла в том, чтобы расширить задачу, чтобы содержать некоторый идентификатор, например. целое число, а затем получить этот экземпляр из обработчика события.

Любая идея, как добиться такого поведения, будет очень желанной.

Заранее спасибо.

+1

Я считаю, что каждый экземпляр службы может иметь не более 1 Task active в каждый момент времени. (Из документа [docs] (https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Service.html): * «Служба создает и управляет ** ** Задачей ... «*.) Эта задача может, разумеется, порождать дополнительные потоки, но затем сама отвечает за их синхронизацию. Вы также можете иметь несколько экземпляров одного класса службы, который, вероятно, вы хотите. Состояние каждого экземпляра службы привязано к состоянию соответствующей задачи, но, в отличие от задач, услуга может быть перезапущена (затем создается новая Задача). –

+0

«Как можно запустить службу более одного раза одновременно». Это неверно. Данный экземпляр службы может выполнять только одну задачу за раз. –

+0

IMHO, 'Service' может запускать несколько' Tasks', только обработчики событий 'Service' будут вызваны на основе состояния' Task'. – stepasite

ответ

1

Во-первых, я бы поставил под вопрос, зачем вам это нужно. Сама задача должна инкапсулировать всю логику, специфичную для своего конкретного экземпляра. Похоже, должно быть лучшее решение того, что вы пытаетесь достичь.

Таким образом, самый очевидный способ сделать это - вернуть Task значение, которое вас интересует.

public class MyService extends Service<Integer> { 

    private int nextId = 0 ; 

    @Override 
    protected Task<Integer> createTask() { 
     private final int id = ++nextId ; 
     return new Task<Integer>() { 
      @Override 
      protected Integer call() throws Exception { 
       // do some work 
       return id; 
      } 
     }; 
    } 
} 

Тогда

Service<Integer> service = new MyService(); 
service.setOnSucceeded(e -> System.out.println("Task " + service.getValue() + " finished")); 

Заметим также, что вы можете переопределить succeeded() метод Task (а также регистрации обработчика с сервисом). Таким образом, вы можете сделать

public class MyService extends Service<Void> { 

    private int nextId = 0; 

    @Override 
    protected Task<Void> createTask() { 
     return new Task<Void>() { 

      final int id = ++nextId ; 

      @Override 
      protected Void call() throws Exception { 
       // do some work 
       return null; 
      } 

      @Override 
      protected void succeeded() { 
       super.succeeded(); 
       System.out.println("Task "+id+" completed successfully"); 
      } 
     }; 
    } 
} 

Метод succeeded() вызывается на FX Application Thread.

Если вы действительно хотите управлять этим через службу, вы можете просто сохранить ссылку на последнюю задачу, которая была запущена в реализации службы. Поскольку Service может запускать только одну задачу за раз, когда служба входит в состояние SUCCEEDED, это гарантированно будет только что завершенной задачей. Например:

public class MyService extends Service<Void> { 

    private Task<Void> mostRecentTask ; 

    @Override 
    protected Task<Void> createTask() { 
     Task<Void> task = new Task<Void>() { 
      @Override 
      protected Void call() throws Exception { 
       // do some work 
       return null; 
      } 
     }; 
     task.setOnRunning(e -> mostRecentTask = task); 
     return task ; 
    } 

    public Task<Void> getMostRecentTask() { 
     return mostRecentTask ; 
    } 
} 

Тогда

MyService service = new MyService(); 
service.setOnSucceeded(e -> { 
    Task<Void> completedTask = service.getMostRecentTask(); 
    // ... 
}); 

Но, как я уже говорил, он чувствует, как должно быть более элегантный способ достичь то, что вы пытаетесь сделать на более фундаментальный уровень.

+0

Большое спасибо James_D за подробный ответ и объяснение. Я думал, что может быть больше задач одновременно. Я имею в виду, что Service.restart() вызывает cancel() для текущей задачи (но отмененное состояние может быть проигнорировано при вызове(), или может потребоваться время для остановки текущей задачи Task), но затем оно создает и запускает новый. Но если действительно гарантировать, что в заданное время может быть только одна запущенная задача, тогда мой ответ не имеет смысла. – stepasite

+0

Как я понимаю, если вы вызываете 'cancel()', текущая задача (если она есть) входит в состояние 'CANCELLED' (независимо от того, действительно ли его метод' call() ') завершает свою работу. Оттуда он никогда не может войти в состояние 'SUCCEEDED', поэтому обработчик' onSucceeded' никогда не может быть вызван для этой задачи. –

+0

Просто написал для этого тестовый код, и это действительно то, что происходит. –