【问题标题】:How to obtain Task instance from Service handler method in JavaFX如何从 JavaFX 中的服务处理程序方法获取任务实例
【发布时间】:2016-07-29 10:13:58
【问题描述】:

我有一个 JavaFX 服务,其中创建了 Task 来做一些工作:

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 以包含一些 id,例如整数,然后能够从事件处理程序中获取该实例。

欢迎任何想法,如何实现这种行为。

提前致谢。

【问题讨论】:

  • 我相信每个服务实例每次最多可能有 1 个任务处于活动状态。 (来自docs“服务创建和管理 a 任务...”。)该任务当然可能产生额外的线程,但它自己负责用于同步它们。您还可以拥有单个服务类的多个实例,这可能是您想要的。每个服务实例的状态都绑定到其对应任务的状态,但与任务不同的是,服务可以重新启动(然后创建一个新任务)。
  • “因为可以同时运行服务不止一次”。这是不正确的。给定的服务实例一次只能执行一项任务。
  • 恕我直言,Service 可以运行多个Tasks,只是Service 事件处理程序将根据Task 的状态被调用。

标签: java javafx service task


【解决方案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"));

还请注意,您可以覆盖Tasksucceeded() 方法(以及向服务注册处理程序)。所以你可以做

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");
            }
        };
    }
}

在 FX 应用程序线程上调用 succeeded() 方法。

如果你真的想通过服务来管理它,你可以只保留对服务实现中启动的最新任务的引用。由于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();
    // ...
});

但是,正如我之前所说,感觉应该有一种更优雅的方式来实现您在更基本的层面上尝试做的任何事情。

【讨论】:

  • 非常感谢 James_D 的详细解答和解释。我认为可以同时有更多的任务。我的意思是 Service.restart() 在当前任务上调用 cancel() (但在 call() 方法中可能会忽略取消状态,或者停止当前正在运行的任务可能需要一些时间),但随后它会创建并运行一个新任务。但是如果真的保证在给定时间只能有一个正在运行的Task,那么我的回答就没有意义了。
  • 据我了解,如果你调用cancel(),当前任务(如果有的话)进入CANCELLED状态(不管它的call()方法是否真的退出)。从那里,它永远无法进入SUCCEEDED 状态,因此永远无法为该任务调用onSucceeded 处理程序。
  • 刚刚为此写了一些测试代码,确实是这样。
  • 你是对的 James_D,Service.onSucceeded() 处理程序永远不会被调用,即使Task.call() 方法成功完成,但Task 的状态是CANCELLED 状态。如果我错了,请更正以下内容:事实上,如果给定Service 有多个Task 实例,则可以同时运行“Task.call()”方法。这意味着程序员有责任正确实现call()方法,以便检查Task是否处于CANCELLED状态;如果是,终止该方法是程序员的责任。
  • 听起来是对的。 reset()restart() 会将当前任务置于CANCELLED 状态(如果它被阻塞则中断它),但call 方法如果没有写入检查取消(或中断)可能会继续执行)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-27
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 2014-09-04
  • 1970-01-01
相关资源
最近更新 更多