Есть ли разница? SwingWorker # publish против SwingUtilities # invokeLater

Допустим, у нас есть длинная / тяжелая задача, которая должна выполняться в фоновом режиме и публиковать свой прогресс или что-то еще в графическом интерфейсе. Я знаю, что эта публикация должна происходить в потоке отправки событий. Вот почему мы используем SwingWorker для этой задачи.

Итак, что мы делаем, это примерно так:

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(), который является обновлением графического интерфейса пользователя, запускается в потоке отправки событий. Насколько они разные?

Конечно, возникает вопрос, почему я должен реализовать метод done() для рабочего, а не вызвать SwingUtilities.invokeLater в конце метода doInBackground? Помимо обработки исключения, которое может быть выдано в doInBackground методе.


person George Z.    schedule 22.10.2020    source источник
comment
Зачем пытаться изобретать велосипед? Методы process (), done () помогают логически разбить обработку. Если вы хотите использовать SwingUtilities.invokeLater (), тогда зачем использовать SwingWorker? просто используйте отдельный поток.   -  person camickr    schedule 22.10.2020
comment
@camickr Это вполне уместный вопрос. Я тоже об этом задумался. Я провел несколько тестов на основе ответа @ Abra, и я понял разницу. Однако эта разница становится меньше, если вместо invokeLater я использую invokeAndWait. Даже при небольшом снижении производительности события публикуются так, как я хочу, и в правильном порядке. Итак, я полагаю, используя Thread из invokeAndWait, я могу добиться поведения SwingWorker. Я прав?   -  person George Z.    schedule 23.10.2020
comment
Я не знаю, как работает процесс объединения. Я всегда считал, что вызов метода process () подобен вызову invokeLater (), поскольку код добавляется в EDT для обработки.   -  person camickr    schedule 23.10.2020


Ответы (1)


Посмотрите javadoc для метода publish() в классе SwingWorker.

Поскольку метод процесса вызывается асинхронно в потоке отправки событий, несколько вызовов метода публикации могут произойти до того, как метод процесса будет выполнен. В целях повышения производительности все эти вызовы объединяются в один вызов с объединенными аргументами.

Вызов SwingUtilities.invokeLater() непосредственно из метода doInBackground() не выполняет объединение - в соответствии с кодом в вашем вопросе. Возможно, вы придумаете причину, по которой слияние необходимо? Также см. Задачи, имеющие промежуточные результаты

Что касается метода done() в классе SwingWorker, о котором вы также спрашивали, я снова отсылаю вас к javadoc

Выполняется в потоке отправки событий после завершения метода doInBackground. Реализация по умолчанию ничего не делает. Подклассы могут переопределить этот метод для выполнения действий завершения в потоке отправки событий. Обратите внимание, что вы можете запросить статус внутри реализации этого метода, чтобы определить результат этой задачи или была ли эта задача отменена.

Таким образом, вам не нужно переопределять метод done(). Лично я обычно добавляю к своему объекту SwingWorker прослушиватель свойств для обработки задач, которые мне нужно выполнить после завершения задачи [SwingWorker]. Но конечно же, YMMV.

person Abra    schedule 22.10.2020