java.lang.Object javax.swing.SwingWorker<T,V>
T
- 此
SwingWorker
的
doInBackground
和
get
方法返回的结果类型
V
- 用于保存此
SwingWorker
的
publish
和
process
方法的中间结果的类型
public abstract class SwingWorker<T,V>
在专用线程中执行长时间 GUI 交互任务的抽象类。
使用 Swing 编写多线程应用程序时,要记住两个约束条件:(有关详细信息,请参阅 How to Use Threads):
这些约束意味着需要时间密集计算操作的 GUI 应用程序至少需要以下两个线程:1) 执行长时间任务的线程; 2) 所有 GUI 相关活动的事件指派线程 (EDT)这涉及到难以实现的线程间通信。
SwingWorker
设计用于需要在后台线程中运行长时间运行任务的情况,并可在完成后或者在处理过程中向 UI 提供更新。SwingWorker
的子类必须实现 doInBackground()
方法,以执行后台计算。
工作流
SwingWorker
的生命周期中包含三个线程:
当前 线程:在此线程上调用 execute()
方法。它调度 SwingWorker
以在 worker 线程上执行并立即返回。可以使用 get
方法等待 SwingWorker
完成。
Worker 线程:在此线程上调用 doInBackground()
方法。所有后台活动都应该在此线程上发生。要通知 PropertyChangeListeners
有关绑定 (bound) 属性的更改,请使用 firePropertyChange
和 getPropertyChangeSupport()
方法。默认情况下,有两个可用的绑定属性:state
和 progress
。
事件指派线程:所有与 Swing 有关的活动都在此线程上发生。SwingWorker
调用 process
和 done()
方法,并通知此线程的所有 PropertyChangeListener
。
通常,当前 线程就是事件指派线程。
在 worker 线程上调用 doInBackground
方法之前,SwingWorker
通知所有 PropertyChangeListener
有关对 StateValue.STARTED
的 state
属性更改。doInBackground
方法完成后,执行 done
方法。然后 SwingWorker
通知所有 PropertyChangeListener
有关对 StateValue.DONE
的 state
属性更改。
SwingWorker
被设计为只执行一次。多次执行 SwingWorker
将不会调用两次 doInBackground
方法。
示例用法
下例说明了最简单的使用范例:在后台完成某些处理,并在处理完成后更新 Swing 组件。
假定想找到“Meaning of Life”并在 JLabel
中显示结果。
final JLabel label; class MeaningOfLifeFinder extends SwingWorker<String, Object> {@Override
public String doInBackground() { return findTheMeaningOfLife(); }@Override
protected void done() { try { label.setText(get()); } catch (Exception ignore) { } } } (new MeaningOfLifeFinder()).execute();
在希望处理已经在事件指派线程 上准备好的数据时,下一个例子很有用。
现在想要查找第一个 N 素数值并在 JTextArea
中显示结果。在计算过程中,想在 JProgressBar
中更新进度。最后,还要将该素数值打印到 System.out
。
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"); } } } JTextArea textArea = new JTextArea(); final JProgressBar progressBar = new JProgressBar(0, 100); PrimeNumbersTask task = new PrimeNumbersTask(textArea, N); task.addPropertyChangeListener( new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if ("progress".equals(evt.getPropertyName())) { progressBar.setValue((Integer)evt.getNewValue()); } } }); task.execute(); System.out.println(task.get()); //prints all prime numbers we have got
因为 SwingWorker
实现了 Runnable
,所以可以将 SwingWorker
提交给 Executor
执行。
嵌套类摘要 | |
---|---|
static class |
SwingWorker.StateValue state 绑定 (bound) 属性的值。 |
构造方法摘要 | |
---|---|
SwingWorker() 构造此 SwingWorker 。 |
方法摘要 | |
---|---|
void |
addPropertyChangeListener(PropertyChangeListener listener) 将 PropertyChangeListener 添加到侦听器列表。 |
boolean |
cancel(boolean mayInterruptIfRunning) 试图取消对此任务的执行。 |
protected abstract T |
doInBackground() 计算结果;如果无法计算结果,则抛出异常。 |
protected void |
done() doInBackground 方法完成后,在事件指派线程 上执行此方法。 |
void |
execute() 调度此 SwingWorker 以便在 worker 线程上执行。 |
void |
firePropertyChange(String propertyName, Object oldValue, Object newValue) 向所有已注册的侦听器报告绑定属性更新。 |
T |
get() 如有必要,等待计算完成,然后获取其结果。 |
T |
get(long timeout, TimeUnit unit) 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。 |
int |
getProgress() 返回 progress 绑定属性。 |
PropertyChangeSupport |
getPropertyChangeSupport() 返回此 SwingWorker 的 PropertyChangeSupport 。 |
SwingWorker.StateValue |
getState() 返回 SwingWorker 状态绑定属性。 |
boolean |
isCancelled() 如果在任务正常完成前将其取消,则返回 true。 |
boolean |
isDone() 如果任务已完成,则返回 true。 |
protected void |
process(List<V> chunks) 在事件指派线程 上异步地从 publish 方法接收数据块。 |
protected void |
publish(V... chunks) 将数据块发送给 process(java.util.List
方法。 |
void |
removePropertyChangeListener(PropertyChangeListener listener) 从侦听器列表中移除一个 PropertyChangeListener 。 |
void |
run() 将此 Future 设置为计算的结果,除非它已经被取消。 |
protected void |
setProgress(int progress) 设置 progress 绑定属性。 |
从类 java.lang.Object 继承的方法 |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
构造方法详细信息 |
---|
public SwingWorker()
SwingWorker
。
方法详细信息 |
---|
protected abstract T doInBackground() throws Exception
注意,此方法只执行一次。
注:此方法在后台线程中执行。
Exception
- 如果无法计算结果
public final void run()
Future
设置为计算的结果,除非它已经被取消。
Runnable
中的
run
RunnableFuture<T>
中的
run
Thread.run()
protected final void publish(V... chunks)
process(java.util.List
)
方法。将从
doInBackground
方法内部使用此方法传送中间结果,以便在
process
方法内部对
事件指派线程 进行处理。
由于在事件指派进程 上异步地调用 process
方法,所以在执行 process
方法之前可以对 publish
方法进行多次调用。为了改进性能,所有这些调用都合并为一个带连接参数的调用。
例如:
publish("1"); publish("2", "3"); publish("4", "5", "6");结果可能为:
process("1", "2", "3", "4", "5", "6")
示例用法。以下代码片断加载某一表格数据,并用它来更新 DefaultTableModel
。注意,从 process
方法内部更换 tableModel 是安全的,因为该方法是在事件指派线程 上调用的。
class TableSwingWorker extends SwingWorker<DefaultTableModel, Object[]> { private final DefaultTableModel tableModel; public TableSwingWorker(DefaultTableModel tableModel) { this.tableModel = tableModel; }@Override
protected DefaultTableModel doInBackground() throws Exception { for (Object[] row = loadData(); ! isCancelled() && row != null; row = loadData()) { publish((Object[]) row); } return tableModel; }@Override
protected void process(List<Object[]> chunks) { for (Object[] row :chunks) { tableModel.addRow(row); } } }
chunks
- 要处理的中间结果
process(java.util.List
)
protected void process(List<V> chunks)
publish
方法接收数据块。
有关详细信息,请参阅 publish(V...)
方法。
chunks
- 要处理的中间结果
publish(V...)
protected void done()
doInBackground
方法完成后,在
事件指派线程 上执行此方法。默认实现不执行任何操作。子类可以重写此方法,以在
事件指派线程 上执行完成操作。注意,可以查询此方法实现内部的状态,以确定此任务的结果或者是否已经取消了此任务。
doInBackground()
,
isCancelled()
,
get()
protected final void setProgress(int progress)
progress
绑定属性。该值应该在 0 到 100 之间。
由于在事件指派线程 上异步地通知 PropertyChangeListener
,所以可以在调用任意 PropertyChangeListener
之前对 setProgress
方法进行多次调用。为了改进性能,所有这些调用都合并为一个只带最后一个调用参数的调用。
例如,以下调用:
setProgress(1); setProgress(2); setProgress(3);结果可能为带有值
3
的单个
PropertyChangeListener
通知。
progress
- 要设置的进度值
IllegalArgumentException
- 如果值不是从 0 到 100
public final int getProgress()
progress
绑定属性。
public final void execute()
SwingWorker
以便在
worker 线程上执行。存在许多可用的
worker 线程。如果所有
worker 线程都忙于处理其他
SwingWorker
,则此
SwingWorker
将被放入等待队列。
注:SwingWorker
被设计为只执行一次。多次执行 SwingWorker
将不会调用两次 doInBackground
方法。
public final boolean cancel(boolean mayInterruptIfRunning)
此方法返回后,对 Future.isDone()
的后续调用将始终返回 true。如果此方法返回 true,则对 Future.isCancelled()
的后续调用将始终返回 true。
mayInterruptIfRunning
- 如果应该中断执行此任务的线程,则为
true;否则允许正在运行的任务运行完成
public final boolean isCancelled()
Future<T>
中的
isCancelled
public final boolean isDone()
public final T get() throws InterruptedException, ExecutionException
注:在此 SwingWorker
完成之前,在事件指派线程 上调用 get
将阻塞所有 事件(包括 repaint)的处理。
想在事件指派线程 上阻塞 SwingWorker
时,建议使用 模式对话框(modal dialog)。
例如:
class SwingWorkerCompletionWaiter extends PropertyChangeListener { private JDialog dialog; public SwingWorkerCompletionWaiter(JDialog dialog) { this.dialog = dialog; } public void propertyChange(PropertyChangeEvent event) { if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) { dialog.setVisible(false); dialog.dispose(); } } } JDialog dialog = new JDialog(owner, true); swingWorker.addPropertyChangeListener( new SwingWorkerCompletionWaiter(dialog)); swingWorker.execute(); //the dialog will be visible until the SwingWorker is done dialog.setVisible(true);
InterruptedException
- 如果当前的线程在等待时被中断
ExecutionException
- 如果计算抛出异常
public final T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
有关更多信息,请参阅 get()
。
timeout
- 等待的最大时间
unit
- timeout 参数的时间单位
InterruptedException
- 如果当前的线程在等待时被中断
ExecutionException
- 如果计算抛出异常
TimeoutException
- 如果等待超时
public final void addPropertyChangeListener(PropertyChangeListener listener)
PropertyChangeListener
添加到侦听器列表。该侦听器是为所有属性注册的。同一侦听器对象可以被添加多次,并且它们被调用的次数将与添加它们的次数相同。如果
listener
为
null
,则不抛出任何异常并且不执行任何操作。
注:此方法只是一个便捷包装器。所有工作都委托给 getPropertyChangeSupport()
返回的 PropertyChangeSupport
。
listener
- 要添加的
PropertyChangeListener
public final void removePropertyChangeListener(PropertyChangeListener listener)
PropertyChangeListener
。此方法移除一个为所有属性注册的
PropertyChangeListener
。如果将
listener
多次添加到同一事件源,则在被移除之后,它获得的通知将减少一次。如果
listener
为
null
,或者没有添加过,则不抛出任何异常并且不执行任何操作。
注:此方法只是一个便捷包装器。所有工作都委托给 getPropertyChangeSupport()
返回的 PropertyChangeSupport
。
listener
- 要移除的
PropertyChangeListener
public final void firePropertyChange(String propertyName, Object oldValue, Object newValue)
old
和
new
相等并且为非 null,则不触发任何事件。
此 SwingWorker
将成为所有已生成事件的源。
在事件指派线程 之外调用此方法时,则在事件指派线程 上异步地通知 PropertyChangeListener
。
注:此方法只是一个便捷包装器。所有工作都委托给 getPropertyChangeSupport()
返回的 PropertyChangeSupport
。
propertyName
- 已更改属性的编程名称
oldValue
- 属性的旧值
newValue
- 属性的新值
public final PropertyChangeSupport getPropertyChangeSupport()
SwingWorker
的
PropertyChangeSupport
。当需要灵活地访问绑定属性支持时,使用此方法。
此 SwingWorker
将成为所有已生成事件的源。
注:如果在事件指派线程 之外调用 firePropertyChange
或 fireIndexedPropertyChange
,则返回的 PropertyChangeSupport
在事件指派线程 上异步地通知所有 PropertyChangeListener
。
SwingWorker
的
PropertyChangeSupport
public final SwingWorker.StateValue getState()
SwingWorker
状态绑定属性。