首页 > 解决方案 > 有什么区别吗?SwingWorker#publish vs SwingUtilities#invokeLater

问题描述

假设我们有一个必须在后台运行的长/繁重的任务,并将其进度或其他任何内容发布到 GUI。我知道这个发布必须发生在事件调度线程上。这就是我们将 aSwingWorker用于任务的原因。

所以,我们所做的是这样的:

public class WorkerTest {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new FlowLayout());

            JLabel label = new JLabel();
            frame.add(label);
            startWorker(label);

            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }

    private static void startWorker(JLabel label) {
        new SwingWorker<Integer, Integer>() {

            @Override
            protected Integer doInBackground() throws Exception {
                for (int i = 0; i < 500; i++) {
                    publish(i);
                    Thread.sleep(500); //Simulate long task
                }
                return null;
            }

            @Override
            protected void process(List<Integer> chunks) {
                Integer integer = chunks.get(0);
                label.setText(String.valueOf(integer));
            }
        }.execute();
    }
}

我的问题是,上述内容与此有何不同:

private static void startWorker(JLabel label) {
    new SwingWorker<Integer, Integer>() {

        @Override
        protected Integer doInBackground() throws Exception {
            for (int i = 0; i < 500; i++) {
                int i2 = i;
                SwingUtilities.invokeLater(() -> {
                    label.setText(String.valueOf(i2));
                });
                Thread.sleep(500); //Simulate long task
            }
            return null;
        }

    }.execute();
}

在这两种情况下,作为label.setText()对 GUI 的更新,都会运行到事件调度线程。它们有何不同?

当然,问题还在于为什么我应该在方法结束时done()对工作人员实施方法而不是调用?除了处理方法中可能抛出的异常。SwingUtilities.invokeLaterdoInBackgrounddoInBackground

标签: javaswingswingworkeredt

解决方案


查看类中的方法的javadocpublish()SwingWorker

因为流程方法是在事件调度线程上异步调用的,所以在流程方法执行之前可能会发生对发布方法的多次调用。出于性能目的,所有这些调用都合并为一个带有串联参数的调用。

SwingUtilities.invokeLater()根据您问题中的代码,直接从方法调用doInBackground()不会执行合并。也许您可以想到需要合并的原因?另请参阅具有中期结果的任务

关于done()类中的方法SwingWorker,你也问过,我再次推荐你参考javadoc

doInBackground 方法完成后在事件调度线程上执行。默认实现什么也不做。子类可以重写此方法以在事件调度线程上执行完成操作。请注意,您可以在此方法的实现内部查询状态,以确定此任务的结果或此任务是否已被取消。

所以你不必重写 method done()。就个人而言,我通常向我的对象添加一个属性侦听器,以处理在 [ ] 任务完成后SwingWorker我需要执行的任务。SwingWorker但是,当然,YMMV。


推荐阅读