| |||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
public abstract class SwingWorker extends Object implements RunnableFuture
An abstract class to perform lengthy GUI-interacting tasks in a dedicated thread.
When writing a multi-threaded application using Swing, there are two constraints to keep in mind: (refer to How to Use Threads for more details):
These constraints mean that a GUI application with time intensive computing needs at least two threads: 1) a thread to perform the lengthy task and 2) the Event Dispatch Thread (EDT) for all GUI-related activities. This involves inter-thread communication which can be tricky to implement.
{@code SwingWorker} is designed for situations where you need to have a long running task run in a background thread and provide updates to the UI either when done, or while processing. Subclasses of {@code SwingWorker} must implement the {@link #doInBackground} method to perform the background computation.
Workflow
There are three threads involved in the life cycle of a {@code SwingWorker} :
Current thread: The {@link #execute} method is called on this thread. It schedules {@code SwingWorker} for the execution on a worker thread and returns immediately. One can wait for the {@code SwingWorker} to complete using the {@link #get get} methods.
Worker thread: The {@link #doInBackground} method is called on this thread. This is where all background activities should happen. To notify {@code PropertyChangeListeners} about bound properties changes use the {@link #firePropertyChange firePropertyChange} and {@link #getPropertyChangeSupport} methods. By default there are two bound properties available: {@code state} and {@code progress}.
Event Dispatch Thread: All Swing related activities occur on this thread. {@code SwingWorker} invokes the {@link #process process} and {@link #done} methods and notifies any {@code PropertyChangeListeners} on this thread.
Often, the Current thread is the Event Dispatch Thread.
Before the {@code doInBackground} method is invoked on a worker thread, {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the {@code state} property change to {@code StateValue.STARTED}. After the {@code doInBackground} method is finished the {@code done} method is executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the {@code state} property change to {@code StateValue.DONE}.
{@code SwingWorker} is only designed to be executed once. Executing a {@code SwingWorker} more than once will not result in invoking the {@code doInBackground} method twice.
Sample Usage
The following example illustrates the simplest use case. Some processing is done in the background and when done you update a Swing component.
Say we want to find the "Meaning of Life" and display the result in a {@code JLabel}.
final JLabel label; class MeaningOfLifeFinder extends SwingWorker<String, Object> { {@code @Override} public String doInBackground() { return findTheMeaningOfLife(); } {@code @Override} protected void done() { try { label.setText(get()); } catch (Exception ignore) { } } } (new MeaningOfLifeFinder()).execute();
The next example is useful in situations where you wish to process data as it is ready on the Event Dispatch Thread.
Now we want to find the first N prime numbers and display the results in a {@code JTextArea}. While this is computing, we want to update our progress in a {@code JProgressBar}. Finally, we also want to print the prime numbers to {@code System.out}.
class PrimeNumbersTask extends SwingWorker<List<Integer>, Integer> { PrimeNumbersTask(JTextArea textArea, int numbersToFind) { //initialize } {@code @Override} public List<Integer> doInBackground() { while (! enough && ! isCancelled()) { number = nextPrimeNumber(); publish(number); setProgress(100 * numbers.size() / numbersToFind); } } return numbers; } {@code @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
Because {@code SwingWorker} implements {@code Runnable}, a {@code SwingWorker} can be submitted to an {@link java.util.concurrent.Executor} for execution.
Nested Class Summary | |
---|---|
static enum |
Values for the bound property. |
Constructor Summary | |
---|---|
Constructs this . |
Method Summary | |
---|---|
void |
Adds a to the listener list. |
boolean |
cancel(boolean mayInterruptIfRunning) |
protected abstract Object |
Computes a result, or throws an exception if unable to do so. |
protected void |
done() Executed on the Event Dispatch Thread after the method is finished. |
void |
execute() Schedules this for execution on a worker thread. |
void |
firePropertyChange(String propertyName, Object oldValue, Object newValue) Reports a bound property update to any registered listeners. |
Object |
get() |
Object |
|
int |
Returns the bound property. |
PropertyChangeSupport |
Returns the for this . |
SwingWorker.StateValue |
getState() Returns the state bound property. |
boolean |
|
boolean |
isDone() |
protected void |
Receives data chunks from the method asynchronously on the Event Dispatch Thread. |
protected void |
Sends data chunks to the javax.swing.SwingWorker.process method. |
void |
Removes a from the listener list. |
void |
run() Sets this to the result of computation unless it has been cancelled. |
protected void |
setProgress(int progress) Sets the bound property. |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructor Detail |
---|
public SwingWorker()
Method Detail |
---|
public final void addPropertyChangeListener(PropertyChangeListener listener)
Note: This is merely a convenience wrapper. All work is delegated to {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
listener
- the {@code PropertyChangeListener} to be addedpublic final boolean cancel(boolean mayInterruptIfRunning)
mayInterruptIfRunning
protected abstract Object doInBackground() throws Exception
Note that this method is executed only once.
Note: this method is executed in a background thread.
Exception
- if unable to compute a resultprotected void done()
public final void execute()
Note: {@code SwingWorker} is only designed to be executed once. Executing a {@code SwingWorker} more than once will not result in invoking the {@code doInBackground} method twice.
public final void firePropertyChange(String propertyName, Object oldValue, Object newValue)
This {@code SwingWorker} will be the source for any generated events.
When called off the Event Dispatch Thread {@code PropertyChangeListeners} are notified asynchronously on the Event Dispatch Thread.
Note: This is merely a convenience wrapper. All work is delegated to {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
propertyName
- the programmatic name of the property that was
changedoldValue
- the old value of the propertynewValue
- the new value of the propertypublic final Object get() throws InterruptedException, ExecutionException
Note: calling {@code get} on the Event Dispatch Thread blocks all events, including repaints, from being processed until this {@code SwingWorker} is complete.
When you want the {@code SwingWorker} to block on the Event Dispatch Thread we recommend that you use a modal dialog.
For example:
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 Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
Please refer to {@link #get} for more details.
timeout
unit
InterruptedException
ExecutionException
TimeoutException
public final int getProgress()
public final PropertyChangeSupport getPropertyChangeSupport()
This {@code SwingWorker} will be the source for any generated events.
Note: The returned {@code PropertyChangeSupport} notifies any {@code PropertyChangeListener}s asynchronously on the Event Dispatch Thread in the event that {@code firePropertyChange} or {@code fireIndexedPropertyChange} are called off the Event Dispatch Thread.
public final SwingWorker.StateValue getState()
public final boolean isCancelled()
public final boolean isDone()
protected void process(List chunks)
Please refer to the {@link #publish} method for more details.
chunks
- intermediate results to processprotected final void publish(Object[] chunks)
Because the {@code process} method is invoked asynchronously on the Event Dispatch Thread multiple invocations to the {@code publish} method might occur before the {@code process} method is executed. For performance purposes all these invocations are coalesced into one invocation with concatenated arguments.
For example:
publish("1"); publish("2", "3"); publish("4", "5", "6");might result in:
process("1", "2", "3", "4", "5", "6")
Sample Usage. This code snippet loads some tabular data and updates {@code DefaultTableModel} with it. Note that it safe to mutate the tableModel from inside the {@code process} method because it is invoked on the Event Dispatch Thread.
class TableSwingWorker extends SwingWorker<DefaultTableModel, Object[]> { private final DefaultTableModel tableModel; public TableSwingWorker(DefaultTableModel tableModel) { this.tableModel = tableModel; } {@code @Override} protected DefaultTableModel doInBackground() throws Exception { for (Object[] row = loadData(); ! isCancelled() && row != null; row = loadData()) { publish((Object[]) row); } return tableModel; } {@code @Override} protected void process(List<Object[]> chunks) { for (Object[] row : chunks) { tableModel.addRow(row); } } }
chunks
- intermediate results to processpublic final void removePropertyChangeListener(PropertyChangeListener listener)
Note: This is merely a convenience wrapper. All work is delegated to {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
listener
- the {@code PropertyChangeListener} to be removedpublic final void run()
protected final void setProgress(int progress)
Because {@code PropertyChangeListener}s are notified asynchronously on the Event Dispatch Thread multiple invocations to the {@code setProgress} method might occur before any {@code PropertyChangeListeners} are invoked. For performance purposes all these invocations are coalesced into one invocation with the last invocation argument only.
For example, the following invokations:
setProgress(1); setProgress(2); setProgress(3);might result in a single {@code PropertyChangeListener} notification with the value {@code 3}.
progress
- the progress value to set
| |||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |