53

Related to my previous question: Call repaint from another class in Java?

I'm new to Java and I've had a look at some tutorials on SwingWorker. Yet, I'm unsure how to implement it with the example code I gave in the previous question.

Can anyone please explain how to use SwingWorker with regards to my code snippet and/or point me towards a decent tutorial? I have looked but I'm not sure I understand yet.

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Relequestual
  • 11,631
  • 6
  • 47
  • 83

2 Answers2

115

Generally, SwingWorker is used to perform long-running tasks in Swing.

Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up, so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive by which prioritizing the other AWT events before running the desired task (in the form of a Runnable).

However, the problem with SwingUtilities is that it didn't allow returning data from the the executed Runnable to the original method. This is what SwingWorker was designed to address.

The Java Tutorial has a section on SwingWorker.

Here's an example where a SwingWorker is used to execute a time-consuming task on a separate thread, and displays a message box a second later with the answer.

First off, a class extending SwingWorker will be made:

class AnswerWorker extends SwingWorker<Integer, Integer>
{
    protected Integer doInBackground() throws Exception
    {
        // Do a time-consuming task.
        Thread.sleep(1000);
        return 42;
    }

    protected void done()
    {
        try
        {
            JOptionPane.showMessageDialog(f, get());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

The return type of the doInBackground and get methods are specified as the first type of the SwingWorker, and the second type is the type used to return for the publish and process methods, which are not used in this example.

Then, in order to invoke the SwingWorker, the execute method is called. In this example, we'll hook an ActionListener to a JButton to execute the AnswerWorker:

JButton b = new JButton("Answer!");
b.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e)
    {
        new AnswerWorker().execute();
    }
});

The above button can be added to a JFrame, and clicked on to get a message box a second later. The following can be used to initialize the GUI for a Swing application:

private void makeGUI()
{
    final JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.getContentPane().setLayout(new FlowLayout());

    // include: "class AnswerWorker" code here.
    // include: "JButton" b code here.

    f.getContentPane().add(b);
    f.getContentPane().add(new JButton("Nothing"));
    f.pack();
    f.setVisible(true);
}

Once the application is run, there will be two buttons. One labeled "Answer!" and another "Nothing". When one clicks on the "Answer!" button, nothing will happen at first, but clicking on the "Nothing" button will work and demonstrate that the GUI is responsive.

And, one second later, the result of the AnswerWorker will appear in the message box.

coobird
  • 159,216
  • 35
  • 211
  • 226
  • I don't believe what I'm doing is a long running task. I have thought of an alternate way to deal with this issue, and will ask in another question. Thanks – Relequestual Apr 23 '09 at 16:47
  • waw! very informative for me also. But what about if I have many request that are returning some data to my Swing Components (GUI) ? Should I recreate all SwingWorker class each of the call? Or there's alternative by using Thread.daemon() @coobird ? :D – gumuruh Apr 20 '12 at 03:12
  • 1
    @gumuruh (If I understand you correctly) Depending on your use case, having a background thread may be a good option, but this would require more information which could be a new question all together. However, one thing that can be said is that according to the documentation, an instance of a `SwingWorker` is designed to have its `execute` method called only once, so re-running the `execute` method would not be an option. – coobird Apr 21 '12 at 07:51
  • @coobird would like to point out the goofup pointed out by selma (the other answerer). Other than that +1. – Surender Thakran Jan 08 '13 at 16:46
  • 1
    @SurenderThakran @Selma Thank you for pointing out the problem in the description of the `SwingUtilities` methods. I've updated the answer to correct the error. – coobird Jan 10 '13 at 14:08
  • @coobird your code actually display the message or notify the user with a dialog when the background process complete. but what if i want a dialog to be there on top of Screen till the background thread finishes ?i.e As Soon as background thread Start, a message is displayed and on completion i want to dispose that alert? how could i achieve this with SwingWorker ? – JAVA May 22 '18 at 06:21
  • 1
    @coobird Why do you say that `invokeLater` was done to keep the UI responsive? I believe you should correct it (`invokeLater` only adds to the FIFO EDT Queue a task to run) (see @dalvarezmartinez1 answer below). Other than that, +1 for the SwingWorker explanation. – hexstorm Mar 17 '21 at 11:15
13

Agree:

Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up.

Do not agree:

so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive..

invokeLater still runs the code on the EDT, and can freeze your UI!! Try this:

SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

At least I, cannot move my mouse once I click the button which triggers the actionPerformed with the above code. Am I missing something?

dalvarezmartinez1
  • 1,385
  • 1
  • 17
  • 26
  • 1
    You are right. Generally invokeLater will be called outside of the event dispatch thread to run something on the event dispatch thread, e.g. a long running task may call invokeLater to alter the display. This is because actions on swing objects must occur from the event thread to maintain thread safety. – dhorn Apr 10 '15 at 03:05