Intro to SwingWorker in Java
SwingWorker Tutorial
When running a program that needs to execute a long-running task, we want to be able to allow the execution of the main application to continue while the work completes in the background. To do so, we use a SwingWorker, which allows creating a background thread to perform our task and communicate to our main application the progress.
Quick Notes About SwingWorker
- The
javax.swing.SwingWorker
class was added to the Java platform in Java SE 6. - Prior to Java SE 6, another class, also called SwingWorker, was widely used for some of the same purposes. The old SwingWorker was not part of the Java platform specification, and was not provided as part of the JDK.
Anatomy of the SwingWorker
There are three threads involved in the life cycle of a SwingWorker:
- Current thread: The
execute()
method is called on this thread. It schedules SwingWorker for the execution on a worker thread and returns immediately. One can wait for the SwingWorker to complete using the get methods. - Worker thread: The
doInBackground()
method is called on this thread. This is where all background activities should happen. To notifyPropertyChangeListeners
about bound properties changes use thefirePropertyChange
andgetPropertyChangeSupport()
methods. By default, there are two bound properties available: state and progress. - Event Dispatch Thread: All Swing related activities occur on this thread. SwingWorker invokes the
process()
anddone()
methods and notifies anyPropertyChangeListeners
on this thread.
When creating a SwingWorker<T, V>, we define two parameters on the constructor; T — the result type returned by this SwingWorker's doInBackground()
and get()
methods, and V the type used for carrying out intermediate results by this SwingWorker's publish()
and process()
methods.
The first parameter is used to inform the done()
method of the result of the background execution, whereas the second parameter is sent to the process()
method to have preliminary results displayed like in a UI.
Vacuous SwingWorker example:
SwingWorker worker = new SwingWorker<Boolean, Integer>() {
@Override
protected Boolean doInBackground() throws Exception {
// Background work
// Value transmitted to done()
return true;
}
@Override
protected void process(List<Integer> chunks) {
// Process results
}
@Override
protected void done() {
// Finish sequence
}
};
Hands-on SwingWorker Example
Now that the theoretical background of the SwingWorker has been covered, we will proceed with a practical example that will show how to find even numbers, while displaying the percentage of progress.
The doInBackground()
method is in charge of executing the part of our code that will take a considerable amount of time to complete.
Inside this method, we have the following options to inform about the execution.
- setProgress(): Sets the percentage value of the overall progress.
- publish(): Sends a chunk of information to the
process()
method that can be retrieved to perform a task. - return: When we finish or end in an inconsistent state, we can stop the background execution by returning the value to the
done()
method.
In our example, we find even numbers and send them to the process()
method to print the findings. Once we finish, we send a boolean result to the done()
method to write the obtained value.
To start the worker we call the method from the class Loader
and we add a loop to detect if the worker has finished. If it has not finished we print the progress and wait a half second before checking again.
In the example, we have a waitFor()
method to make the program take longer for didactic purpose.
Compiling the Example:
To compile the code from a terminal you just need to execute the javac compiler as shown below.
Executing the Example:
To execute the compiled code from the terminal, you just need to run the Java code as shown below. The example shows the expected output of the code.
Complete SwingWorker Example:
import javax.swing.*;
import java.util.List;
/**
* Created by Isai B. Cicourel
*/
public class Loader {
/**
* Creates an Example SwingWorker
*/
public SwingWorker createWorker() {
return new SwingWorker<Boolean, Integer>() {
@Override
protected Boolean doInBackground() throws Exception {
// Start Progress
setProgress(0);
waitFor(500);
// Example Loop
for (int iCount = 1; iCount <= 20; iCount++) {
// Is an even number?
if (iCount % 2 == 0) {
publish(iCount);
}
// Set Progress
setProgress((iCount * 100) / 20);
waitFor(250);
}
// Finished
return true;
}
@Override
protected void process(List<Integer> chunks) {
// Get Info
for (int number : chunks) {
System.out.println("Found even number: " + number);
}
}
@Override
protected void done() {
boolean bStatus = false;
try {
bStatus = get();
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("Finished with status " + bStatus);
}
};
} // End of Method: createWorker()
/**
* Wait the given time in milliseconds
* @param iMillis
*/
private void waitFor (int iMillis) {
try {
Thread.sleep(iMillis);
}
catch (Exception ex) {
System.err.println(ex);
}
} // End of Method: waitFor()
public static void main(String[] args) {
// Create the worker
Loader l = new Loader();
SwingWorker work = l.createWorker();
work.execute();
// Wait for it to finish
while (!work.isDone()) {
// Show Progress
try {
int iProgress = work.getProgress();
System.out.println("Progress %" + iProgress);
Thread.sleep(500);
}
catch (Exception ex) {
System.err.println(ex);
}
} // End of Loop: while (!work.isDone())
} // End of: main()
} // End of Class definition