英文:
Is there any difference? SwingWorker#publish vs SwingUtilities#invokeLater
问题
以下是翻译好的部分:
首先的代码示例使用了 SwingWorker
来在后台执行一个长时间/繁重的任务,并将其进度或其他内容发布到 GUI 上。这里使用 SwingWorker
的原因是需要在事件分发线程上进行 GUI 更新。
代码示例如下:
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); // 模拟长时间任务
}
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); // 模拟长时间任务
}
return null;
}
}.execute();
}
这两种情况下,label.setText()
都是在事件分发线程上运行以更新 GUI。它们有何不同?
当然,还有一个问题,为什么我应该在 worker 中实现一个 done()
方法,而不是在 doInBackground
方法的末尾调用 SwingUtilities.invokeLater
呢?除了可能在 doInBackground
方法中抛出的异常的处理之外。
英文:
Let's say we have a long/heavy task that must run in background, and publish its progress or whatever to the GUI. I know that this publishing must happen on the event dispatch thread. That's why we use a SwingWorker
for the task.
So, what we do, is something like this:
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();
}
}
My question is, how the above differs from this:
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();
}
In both cases, the label.setText()
which is an update to the GUI, runs to the Event dispatch thread. How are they different?
Of course, the question stands also why should I implement a done()
method to the worker vs calling SwingUtilities.invokeLater
at the end of the doInBackground
method? Besides handling the exception possibly thrown in doInBackground
method.
答案1
得分: 2
请查阅 SwingWorker
类中 publish()
方法的 javadoc。
> 由于进程方法在事件分发线程上异步调用,可能会在执行进程方法之前多次调用发布方法。出于性能目的,所有这些调用都会合并为一个具有连接参数的调用。
直接在 doInBackground()
方法中调用 SwingUtilities.invokeLater()
不执行合并,根据您问题中的代码。也许您可以思考为什么合并是必要的?还请参考具有中间结果的任务。
关于 SwingWorker
类中您还询问过的 done()
方法,我再次引用 javadoc 中的内容:
> 在 doInBackground
方法完成后,在事件分发线程上执行。默认实现不执行任何操作。子类可以重写此方法以在事件分发线程上执行完成操作。请注意,您可以在此方法的实现内部查询状态,以确定此任务的结果或此任务是否已被取消。
所以您不必重写 done()
方法。就个人而言,我通常会为我的 SwingWorker
对象添加一个属性监听器,以处理在 [SwingWorker
] 任务完成后需要执行的任务。当然,这因人而异。
英文:
Look at the javadoc for method publish()
in class SwingWorker
.
> Because the process method is invoked asynchronously on the Event Dispatch Thread multiple invocations to the publish method might occur before the process method is executed. For performance purposes all these invocations are coalesced into one invocation with concatenated arguments.
Calling SwingUtilities.invokeLater()
directly from method doInBackground()
does not perform the coalescing – according to the code in your question. Perhaps you can think of a reason why the coalescing is necessary? Also refer to Tasks that Have Interim Results
Regarding method done()
in class SwingWorker
, that you also asked about, again I refer you to the javadoc
> Executed on the Event Dispatch Thread after the doInBackground method is finished. The default implementation does nothing. Subclasses may override this method to perform completion actions on the Event Dispatch Thread. Note that you can query status inside the implementation of this method to determine the result of this task or whether this task has been cancelled.
So you don't have to override method done()
. Personally, I usually add a property listener to my SwingWorker
object for handling tasks that I need to perform once the [SwingWorker
] task has completed. But of-course, YMMV.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论