Skip to main content

Did you know that SwingWorker can send progress status?

Posted by mkarg on January 3, 2010 at 7:07 AM PST

Java 6 comes with SwingWorker as an integral part of the JRE (yes, you no more need to download it). And THAT version of SwingWorker can send progress status while the background work still is in progress. Using this new feature, it is possible to do a lengthy background operation that reports its status from time to time. For example: While loading thousands of rows from the server (which might need several seconds) you could return once a second so-called "chunks" of let's say ten or hundred rows and visualize them meanwhile. That means, the users sees how more and more data gets shown. That is much better than just showing an hourglass pointer. And it is really much better than not showing anything and even do not redraw. Since the SwingWorker correctly uses Swing's event thread to draw the "chunks", you will even see no repaint problems. Everything just looks pretty nice and behaves really responsive (just as you would love it if you would be a user).

Here is a small test program that demonstrates the behaviour. It shows a frame with a slider and a button. A background thread needs one minute to finish, and sends a progress status (here: the current number of seconds it was running so far) to the Swing event thread. That thread then paints the slider in the new position. The cool thing is that the UI is completely responsive. That means, not only the UI is correctly repainted while the background thread is working (or sleeping), but you can also press the button in that time and will not see any hanging repaint updates, etc. Just run the code and press the button, and then move around the message box. You will see that in the background the slider moves and correctly paints, still.

 public final class ProgressStateSample {

public static final void main(final String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

final JFrame sampleFrame = new JFrame();
sampleFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

final JPanel body = new JPanel();
body.setLayout(new BorderLayout());

final JSlider slider = new JSlider(0, 60);
body.add(slider, BorderLayout.NORTH);

@SuppressWarnings("serial")
final Action action = new AbstractAction("Click Here") {

@Override
public final void actionPerformed(final ActionEvent e) {
JOptionPane.showMessageDialog(sampleFrame, "As you can see, the UI is responsive and there are no repaint problems!");
}

};

final JButton button = new JButton(action);
body.add(button, BorderLayout.SOUTH);

sampleFrame.add(body);
sampleFrame.pack();
sampleFrame.setVisible(true);

final SwingWorker<Integer, Integer> backgroundWork = new SwingWorker<Integer, Integer>() {

@Override
protected final Integer doInBackground() throws Exception {
for (int i = 0; i < 61; i++) {
Thread.sleep(1000);
this.publish(i);
}

return 60;
}

@Override
protected final void process(final List<Integer> chunks) {
slider.setValue(chunks.get(0));
}

};

backgroundWork.execute();
}
}