JavaFX8 中的任务链:在 onSucceeded 完成上一个任务后启动下一个任务

Rap*_*oth 2 concurrency javafx-8

我对 JavaFX8 相当陌生,面临以下问题。在我当前用于文档处理/编辑的应用程序中,我有两个相当昂贵的任务。打开文档并保存文档。

我的应用程序有“导入下一个”、“导出当前”和“导出当前并导入下一个”按钮。对于导入和导出,我有两个结构如下的任务:

    private class Export extends Task<Void> {
    public Export() {
        this.setOnRunning(event -> {
            // do stuff (change cursor etc)
        });

        this.setOnFailed(event -> {
            // do stuff, eg. show error box
        });

        this.setOnSucceeded(event -> {
            // do stuff
        });
    }

    @Override
    protected Void call() throws Exception {
        // do expensive stuff
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用 提交任务Executors.newSingleThreadExecutor();

对于“导出当前并导入下一个”功能,我的目标是将导出和导入任务提交给执行器,但我的导入任务仅应在导出任务成功并且给出的事件处理程序setOnSucceedded(在 GUI 上运行)的情况下运行线程)完成。如果导出失败,则加载下一个文档没有任何意义,因为需要用户交互。如何才能实现这一目标?

首先,我厌倦了该call方法中的整个逻辑/错误处理,但这不起作用,因为我无法从此方法更改 GUI(即显示错误框)。

作为解决方法,我在导出任务的最后一行手动提交导入任务setOnSucceeded,但这不是很灵活,因为我想确保该任务仅导出(无需后续导入)...

Jam*_*s_D 5

不要setOnXXXTask子类构造函数中调用处理程序属性方法。这些实际上设置了任务的属性,因此如果您还从其他地方调用这些方法,您将替换您在类本身中实现的功能,而不是添加到其中。

相反,重写受保护的便捷方法:

public class Export extends Task<Void> {

    @Override
    protected void succeeded() {
        super.succeeded();
        // do stuff...
    }

    @Override
    protected void running() {
        super.running();
        // do stuff...
    }

    @Override
    protected void failed() {
        super.failed();
        // do stuff...
    }

    @Override
    protected Void call() {
        // do expensive stuff....
        return null ;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以安全地setOnXXX(...)在类外部使用Export,而不会破坏其功能:

Export export = new Export();
export.setOnSucceeded(e -> {
    Import import = new Import();
    executor.submit(import);
});
executor.submit(export);
Run Code Online (Sandbox Code Playgroud)

这将链接任务的逻辑放在实际创建任务的位置,这似乎是正确的位置。

请注意,为状态更改提供多个处理程序的另一种方法是使用以下方法注册侦听器stateProperty()

Export export = new Export();
export.stateProperty().addListener((obs, oldState, newState) -> {
    if (newState == Worker.State.SUCCEEDED) {
        // ...
    }
});
Run Code Online (Sandbox Code Playgroud)

从测试来看,这些不同机制的执行顺序是:

  1. 状态监听器
  2. onSucceeded处理程序
  3. 方法Task.succeeded

所有这些都在 FX 应用程序线程上执行。

因此,如果您希望子类中的代码在外部添加的处理程序之前Task执行,请执行以下操作

public class Export extends Task<Void> {

    public Export() {
        stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == Worker.State.RUNNING) {
                // do stuff
            } else if (newState == Worker.State.SUCCEEDED) {
                // do stuff
            } else if (newState == Worker.State.FAILED) {
                // do stuff
            }
        });
    }

    @Override
    public Void call() {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,您可以在方法中实现整个逻辑call:如果您需要与 UI 交互,您可以将这些调用包装在Platform.runLater(() -> {});. 然而,像您所做的那样将功能分成不同的任务可能会更干净。