2016-01-16 42 views
0

我对JavaFX8颇为陌生,在我当前正在进行文档处理/编辑的App中面临以下问题。我有两个相当昂贵的任务,即打开e文件和保存文件。JavaFX8中的任务链接:开始下一个onSucceeded之后的任务完成前一个任务

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

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

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

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

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

解决方法是,我在导出任务的“setOnSucceeded”的最后一行手动提交导入任务,但不是非常灵活,因为我想要起诉此任务也仅用于导出(没有随后导入)...

回答

1

不要在您的Task子类构造函数中调用处理程序属性方法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 ; 
    } 
} 

现在你可以放心地使用setOnXXX(...)内外兼修的Export课,而不会破坏它的功能:

Export export = new Export(); 
export.setOnSucceeded(e -> { 
    Import import = new Import(); 
    executor.submit(import); 
}); 
executor.submit(export); 

这使逻辑链接的任务在你实际创造它们的地方,这似乎是正确的地方。

注意,另一种方式来对状态的改变提供多个处理程序是与注册听众stateProperty()

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

从测试,它出现的这些不同的机制的执行顺序是:

  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() { 
     // ... 
    } 
} 

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

+0

在你的例子中,“setOnSucceeded()”中的行会覆盖之前在“succeeded()”中给出的代码吗?或者我在执行他们的订单? –

+0

它不会替换'succeeded()'方法中的代码。从一个快速测试看来,处理器'setOnSucceeded()'首先被执行。两者都在FX应用程序线程上执行。 –

+0

好的,那么它并没有真正解决问题,因为下一个任务应该在任务定义中提供的代码之后提交(即,在您的成功() - 方法中)完成 –

相关问题