将进度信息从Callable传播到Task

时间:2017-11-21 18:14:00

标签: java javafx

我正在使用javafx.concurrent.Task在我们的JavaFX应用程序中完成后台工作。

在单元测试此类任务时,我通常只调用task.call()。与task.run()相比,这不会改变任务的状态(另请参阅here请投票取消删除)。如果我不调用任何update..()方法,即使没有运行JavaFX平台,我也可以调用task.call()

我想重构这种方法独立于正在运行的JavaFX环境,并且call()方法的实现移动到JavaFX独立{ {1}}。

测试这个独立的类会更简单,并且可以在任务中轻松使用这个可调用的类。

现在的问题是如何更新可调用的进度并将此进度信息传播到调用任务?

Callable通过受保护的Task方法进行更新,并且不使用专用对象(例如update...()),这样可以轻松地委派此功能。

1 个答案:

答案 0 :(得分:3)

首先请注意,虽然javafx.concurrent包依赖于FX Toolkit运行,但JavaFX Properties类没有这样的要求。因此,只要您只从单个线程访问JavaFX属性,在您想要使用的类中使用JavaFX属性就行是完全有效的,而无需运行FX Toolkit。

例如,您可以这样做:

public class MyProcess implements Callable<SomeData> {

    private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper();

    public ReadOnlyDoubleProperty progressProperty() {
        return progress.getReadOnlyProperty() ;
    }

    public final double getProgress() {
        return progressProperty().get();
    }

    @Override
    public SomeData call() {
        final int numSteps = ... ;
        SomeData data = new SomeData();
        for (int i = 0 ; i < numSteps ; i++) {
            progress.set(1.0*i/numSteps);
            data.update(i);
        }
        return data ;
    }
}

这个类运行正常,不依赖于FX Toolkit的运行。您可以按如下方式将其包装在任务中:

public class MyProcessTask extends Task<SomeData> {

    @Override
    protected SomeData call() throws Exception {
        MyProcess process = new MyProcess();
        process.progressProperty().addListener((obs, oldProgress, newProgress) ->
            updateProgress(newProgress, 1.0));
        return process.call();
    }
}

如果您希望Callable实现在API级别上独立于JavaFX(不仅仅是在不要求工具包运行的意义上),使用{实现适当的回调非常容易。 {1}} API。例如,您可以将上面的内容重构为

java.util.function

public class MyProcess implements Callable<SomeData> {

    private DoubleConsumer progressUpdate = d -> {} ; // no-op
    private double progress ;

    public void setProgressUpdate(DoubleConsumer progressUpdate) {
        this.progressUpdate = progressUpdate ;
    }

    public final double getProgress() {
        return progress;
    }

    private void setProgress(double progress) {
        this.progress = progress ;
        progressUpdate.accept(progress);
    }

    @Override
    public SomeData call() {
        final int numSteps = ... ;
        SomeData data = new SomeData();
        for (int i = 0 ; i < numSteps ; i++) {
            setProgress(1.0*i/numSteps) ;
            data.update(i);
        }
        return data ;
    }
}