【发布时间】:2020-10-22 09:58:49
【问题描述】:
假设我们有一个长期/繁重的任务,必须在后台运行,并将其进度或其他任何内容发布到 GUI。我知道这个发布必须发生在事件调度线程上。这就是我们使用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();
}
在这两种情况下,作为 GUI 更新的 label.setText() 都会运行到事件调度线程。它们有何不同?
当然,问题还在于为什么我应该对工作人员实现done() 方法而不是在doInBackground 方法的末尾调用SwingUtilities.invokeLater?除了处理doInBackground方法中可能抛出的异常。
【问题讨论】:
-
为什么要重新发明轮子? process()、done() 方法有助于在逻辑上分解处理。如果你想使用 SwingUtilities.invokeLater() 那么为什么要使用 SwingWorker?只需使用单独的线程。
-
@camickr 这是一个非常有效的问题。我也想知道。我根据@Abra 的回答做了一些测试,发现了区别。但是,如果我使用
invokeAndWait而不是invokeLater,那么这种差异会变得更小。即使性能有小幅下降,事件也会按照我想要的方式以正确的顺序发布。所以,我想在invokeAndWait中使用Thread,我可以完成SwingWorker的行为。我说的对吗? -
我不知道合并过程是如何工作的。我一直认为调用 process() 方法就像调用 invokeLater() 一样,因为代码被添加到 EDT 进行处理。
标签: java swing swingworker edt