Concurrency JavaFX8中的任务链接:OnSucceed完成上一个任务后启动下一个任务

Concurrency JavaFX8中的任务链接:OnSucceed完成上一个任务后启动下一个任务,concurrency,javafx-8,Concurrency,Javafx 8,我是JavaFX8的新手,面临以下问题。在我当前用于文档处理/编辑的应用程序中,我有两个相当昂贵的任务。打开文档并保存文档 我的应用程序有“导入下一个”、“导出当前”和“导出当前和导入下一个”按钮。对于导入和导出,我有以下结构的两项任务: private class Export extends Task<Void> { public Export() { this.setOnRunning(event -> { // d

我是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;
    }
}
私有类导出扩展任务{
公共出口(){
此.setOnRunning(事件->{
//做一些事情(改变光标等)
});
此.seton失败(事件->{
//做一些事情,例如显示错误框
});
此.setOnSucceed(事件->{
//做事
});
}
@凌驾
受保护的Void调用()引发异常{
//做昂贵的事
返回null;
}
}
我使用Executors.newSingleThreadExecutor()提交任务

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

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


作为解决方法,我在导出任务中的
setOnSucceeded
的最后一行手动提交导入任务,但这不是很灵活,因为我希望确保此任务仅导出(不进行后续导入)

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

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

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 ;
    }
}
这将把链接任务的逻辑放在您实际创建任务的位置,这似乎是正确的位置

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

通过测试,这些不同机制的执行顺序似乎是:

  • 状态侦听器
  • 继承的
    处理程序
  • 任务。成功
    方法
  • 所有这些都在FX应用程序线程上执行

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

    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() {
            // ...
        }
    }
    
    公共类导出扩展任务{
    公共出口(){
    stateProperty().addListener((obs、oldState、newState)->{
    if(newState==Worker.State.RUNNING){
    //做事
    }else if(newState==Worker.State.successed){
    //做事
    }else if(newState==Worker.State.FAILED){
    //做事
    }
    });
    }
    @凌驾
    公开作废通知(){
    // ...
    }
    }
    

    最后,您可以在
    调用
    方法中实现整个逻辑:如果需要与UI交互,您可以将这些调用包装在
    平台.runLater(()->{})中。但是,将功能划分为不同的任务可能会更干净。

    在您的示例中,“setOnSucceeded()”中的行是否会覆盖“Succeeded()”中之前给出的代码?或者按哪个顺序执行?它不会替换
    successed()
    方法中的代码。通过快速测试,似乎首先执行处理程序
    setOnSucceeded()
    。这两个任务都是在FX应用程序线程上执行的。好的,那么它并不能真正解决问题,因为下一个任务应该在任务定义中提供的代码(即,在successed()方法中)完成后提交
    Export export = new Export();
    export.stateProperty().addListener((obs, oldState, newState) -> {
        if (newState == Worker.State.SUCCEEDED) {
            // ...
        }
    });
    
    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() {
            // ...
        }
    }