SwingWorker not on Java 6.0
SwingWorker is useful when a time-consuming task has to be performed following a user-interaction event (for example, parsing a huge XML File, on pressing a Button). The most straightforward way to do it is :
[sourcecode language='java']
private Document doc;
…
JButton button = new JButton(“Open XML”);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doc = loadXML();
}
});
[/sourcecode]
This will work, but unfortunately, the loadXML() method will be called in the same thread as the main Swing thread, so if the method needs time to perform, the GUI will freeze during this time.
[sourcecode language='java']
package com.wsjoung.demo;
import javax.swing.SwingUtilities;
public abstract class SwingWorker {
// see getValue(), setValue()
private Object value;
private Thread thread;
private static class ThreadVar {
private Thread thread;
ThreadVar(Thread t) { thread = t; }
synchronized Thread get() { return thread; }
synchronized void clear() { thread = null; }
}
private ThreadVar threadVar;
protected synchronized Object getValue() {
return value;
}
private synchronized void setValue(Object x) {
value = x;
}
public abstract Object construct();
public void finished() {
}
public void interrupt() {
Thread t = threadVar.get();
if (t != null) {
t.interrupt();
}
threadVar.clear();
}
public Object get() {
while (true) {
Thread t = threadVar.get();
if (t == null) {
return getValue();
}
try {
t.join();
}
catch (InterruptedException e) {
// propagate
Thread.currentThread().interrupt();
return null;
}
}
}
public SwingWorker() {
final Runnable doFinished = new Runnable() {
public void run() { finished(); }
};
Runnable doConstruct = new Runnable() {
public void run() {
try {
setValue(construct());
}
finally {
threadVar.clear();
}
SwingUtilities.invokeLater(doFinished);
}
};
Thread t = new Thread(doConstruct);
threadVar = new ThreadVar(t);
t.start();
}
}
[/sourcecode]
[sourcecode language='java']
public class Task{
public void go() {
current = 0;
final SwingWorker worker = new SwingWorker() {
public Object construct() {
return new ActualTask();
}
};
}
class ActualTask {
ActualTask() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
}
[/sourcecode]
Task.java
Right, it’s in Java6.0 but what if we do not want, and we do not want clients upgrade VM.
But SwingWorker IS in Java 6.0.
That seems overly complex. Why not just create a Runnable and do SwingUtilities.invokeLater()?
SwingWorker for Java 1.4 is contributed by SUN :
http://java.sun.com/products/jfc/tsc/articles/threads/src/SwingWorker.java
A much better SwingWorker exists in 1.5 in the SwingLabs API:
http://www.swinglabs.org/