JavaFX中的Platform.runLater和Task


88

我一直在对此进行一些研究,但至少可以说我还是很困惑。

谁能给我一个何时使用Task以及何时使用的具体示例Platform.runLater(Runnable);?到底有什么区别?何时使用这些方法是否有黄金法则?

如果我错了,也可以纠正我,但是这两个“对象”不是在GUI主线程内(用于更新GUI)创建另一个线程的方法吗?

Answers:


108

使用Platform.runLater(...)快速和简单的操作和Task复杂的和大的操作。

示例:为什么我们不能Platform.runLater(...)用于长计算(摘自以下参考资料)。

问题:后台线程仅从0到1百万计数,并更新UI中的进度条。

使用代码Platform.runLater(...)

final ProgressBar bar = new ProgressBar();
new Thread(new Runnable() {
    @Override public void run() {
    for (int i = 1; i <= 1000000; i++) {
        final int counter = i;
        Platform.runLater(new Runnable() {
            @Override public void run() {
                bar.setProgress(counter / 1000000.0);
            }
        });
    }
}).start();

这是可怕的代码块,是对自然的犯罪(通常是对程序的犯罪)。首先,仅仅看一下Runnables的这种双重嵌套,就会失去脑细胞。其次,它将用很少的Runnable淹没事件队列-实际上是一百万个。显然,我们需要一些API来简化编写后台工作程序的工作,然后再与UI通信。

使用Task的代码:

Task task = new Task<Void>() {
    @Override public Void call() {
        static final int max = 1000000;
        for (int i = 1; i <= max; i++) {
            updateProgress(i, max);
        }
        return null;
    }
};

ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(task.progressProperty());
new Thread(task).start();

它没有遭受先前代码中显示的任何缺陷

参考: JavaFX 2.0中的辅助线程


Ensemble App链接中的“任务示例”将不再起作用。
Cugomastik 2014年

这是正确的链接,但我不能对其进行编辑,因为编辑仅2个字符。
Aerus

29
说一个用于“快速”操作,另一个用于“复杂”操作有点误导。毕竟,主要区别在于,一个允许从其他地方在JFX GUI线程中运行代码,而另一个却截然相反-允许从GUI线程在后台线程中运行代码(增加了能够在此过程中与GUI线程通信)。
谢尔盖·塔切诺夫

我想保存每个场景的图像-这需要时间。通过Task在单独的线程上运行时,会引起问题吗?我了解所有与gui相关的工作都需要在FxApplication线程上进行。
StephenBoesch 2015年

@ Sergey-Tachenov因此,如果您想要做的不仅仅是更新诸如进度之类的单个属性,您还可以使用Task线程中的runLater()更新GUI线程吗?
simpleuser

58
  • Platform.runLater注意:如果需要从非GUI线程更新GUI组件,则可以使用该组件将更新放入队列,并且该更新将由GUI线程尽快处理。
  • Task实现了一个Worker接口,该接口在您需要在GUI线程外运行长任务(以避免冻结应用程序)但在某个阶段仍需要与GUI交互时使用。

如果您熟悉Swing,则前者等效于SwingUtilities.invokeLater,而后者等效于的概念SwingWorker

Taskjavadoc提供了许多示例,这些示例应阐明如何使用它们。您也可以参考并发教程


谢谢您能举一个如何使用平台的小例子吗?您可以在gui线程之外使用它吗?而在文档的任务的例子是真的不清楚
马克·拉斯穆森

是的,Platform.runLater可以在GUI线程之外使用-这是其主要目的。您可能会发现本教程的任务比javadoc更具信息性。
assylias

3
您可以这样使用Platform.runLater:Platform.runLater(new Runnable() {public void run() {updateYourGuiHere();}});
assylias 2012年

我将如何使用GUI组件,然后=:S
Marc Rasmussen 2012年

1
@KevinMeredith不应在FX线程上调用Task的调用方法-但Task提供了在FX线程上运行的桥接方法(updateProgress等)。有关更多信息,请参见javadoc和教程。
assylias 2013年

12

现在可以将其更改为lambda版本

@Override
public void actionPerformed(ActionEvent e) {
    Platform.runLater(() -> {
        try {
            //an event with a button maybe
            System.out.println("button is clicked");
        } catch (IOException | COSVisitorException ex) {
            Exceptions.printStackTrace(ex);
        }
    });
}

8
如果要处理GUI事件,那么您就在GUI线程中。那么为什么要使用runLater()?
TFuto 2015年

没错,Caglar的答案是试图突出使​​用lambda表达式,但不一定要给出一个很好的编码示例。我使用Platform.runLater(() -> progressBar.setProgress(X/Y));
Taelsin

2

使用显式Platform.runLater()的一个原因可能是您将ui中的属性绑定到服务(结果)属性。因此,如果更新绑定的服务属性,则必须通过runLater()执行此操作:

在UI线程中也称为JavaFX Application线程:

...    
listView.itemsProperty().bind(myListService.resultProperty());
...

在服务实现中(后台工作者):

...
Platform.runLater(() -> result.add("Element " + finalI));
...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.