JavaFX中的SwingWorker等价物是什么?

14

JavaFX中是否有类似于Java SwingWorker类的替代品?

我知道JavaFX Task,但是它只能发布字符串消息或进度。我只想像使用SwingWorker一样在GUI线程中调用方法(通过发布任意类型的消息)。

以下是我所说的示例:

class PrimeNumbersTask extends
         SwingWorker<List<Integer>, Integer> {
     PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
         //initialize
     }

      @Override
     public List<Integer> doInBackground() {
         while (! enough && ! isCancelled()) {
                 number = nextPrimeNumber();
                 publish(number);
                 setProgress(100 * numbers.size() / numbersToFind);
             }
         }
         return numbers;
     }

      @Override
     protected void process(List<Integer> chunks) {
         for (int number : chunks) {
             textArea.append(number + "\n"); // HERE: execute in GUI thread
         }
     }
 }

解决方案

非常感谢你的答案。我在寻找的解决方案是使用Platform.runLater(Runnable guiUpdater)

3个回答

10

我会将您的SwingWorker重写如下:

class PrimeNumbersTask extends Task<List<Integer>> {
    PrimeNumbersTask(TextArea textArea, int numbersToFind) {
        // initialize
    }

    @Override
    protected List<Integer> call() throws Exception {
        while (!enough && !isCancelled()) {
            number = nextPrimeNumber();
            updateMessage(Integer.toString(number));
            updateProgress(numbers.size(), numbersToFind);
        }
        return numbers;
    }
}

使用方法:

TextArea textArea = new TextArea();
PrimeNumbersTask task = new PrimeNumbersTask(numbersToFind);
task.messageProperty().addListener((w, o, n)->textArea.appendText(n + "\n"));
new Thread(task).start(); // which would actually start task on a new thread 

解释:

是的,在JavaFX中我们没有像SwingWorker那样的publish()方法,但在您的情况下,使用updateMessage()已经足够了。我们可以注册一个监听器来监听这个属性,并且每次消息更新时添加一个新行。

如果这还不够,您可以随时使用Platform.runLater()来安排GUI更新。如果您正在进行过多的GUI更新且GUI线程速度变慢,您可以使用以下习惯用语:Throttling javafx gui updates


我已经在考虑那个解决方案,但对我来说更像是一种变通方法(它可以在主要示例中工作,但不是每种情况都适用)。而Platform.runLater()方法则正是我正在寻找的。 - Crine
你刚刚创建了一个新的任务实例,但没有启动任何PrimeNumbersTask的后台工作。我猜需要将其放在线程上并启动它。否则,如果只调用task.run(),GUI会被阻塞。我自己添加了缺失的代码:new Thread(task).start(); 如果这样可以的话,请告诉我。 - Ewoks

0
除了updateMessage方法只能传递字符串之外,还有updateValue方法可以传递整个对象,因此我认为您可以以类似的方式使用它。这种方法在Task文档的“返回部分结果的任务”一节中有描述。另一种方法是Platform.runLater()方法,也在其他答案中提到。
请注意,这些方法之间的一个重要区别是,第一个方法会合并结果,这意味着对于多个频繁的updateValue调用,为了防止洪水般地占用FX线程,可能会省略一些调用。
另一方面,Platform.runLater方法将发送所有中间结果,但由于高频更新可能会危及FX线程,因此可能需要额外的努力手动避免这种情况,就像@eckig在他的回答中建议的那样,指向Throttling javafx gui updates

-1

永远不要使用 SwingWorker。在 SwingWorker.java 源代码中的这段代码应该足以说明不要使用它:

private static final int MAX_WORKER_THREADS = 10;

相反,你应该熟悉Executor和与之相关的服务。

这与JavaFX无关,只是纯粹的Java。然而,你的问题与JavaFX有关。以下是一个示例,演示如何使用Platform.runLater()在JavaFX应用程序线程中更新UI


明白您的不喜欢,但我只是在想,也许SwingWorker在处理从非EDT到EDT(process)的数据块时比每次创建Runnable更有效率。浏览一下publish的源代码,我看到涉及一个名为AccumulativeRunnable的实例,在包sun.swing中。不确定这意味着什么,作为一个完全的JavaFX新手,我也不知道是否会看到任何可比较的东西... - mike rodent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接