0

In my application, I would like to do something like

  • show a wait dialog, where some preparatory task is happening in the background
  • after that preparatory task is done, close the dialog
  • when dialog is closed (or SwingWorker.done(), execute the rest of my code)

I started noticing problems: the code after my new WaitScreen().execute(); was being executed before it and I had to manually close the dialog to trigger the done().

To try to make sense of it, I took some simpler code, made it my own, and used it to emulate my problem. Here it is:

import java.awt.EventQueue; import java.awt.BorderLayout;

import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public class SwingWorkerDialogTest 
{

    public static void main(String[] args) 
    {
        new SwingWorkerDialogTest();
    }
    private JDialog dialog;

    public SwingWorkerDialogTest() 
    {
        EventQueue.invokeLater(new Runnable() 
        {
            @Override
            public void run() 
            {
                try 
                {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                }
                catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) 
                {

                }
                catch (javax.swing.UnsupportedLookAndFeelException ex) 
                {

                }

                MyWorker worker = new MyWorker(true);
                worker.execute();
                new MyWorker(false).execute();
            }
        });
    }

    public class MyWorker extends SwingWorker<Object, Object> 
    {
        private boolean runGUI;
        public MyWorker(boolean b) { this.runGUI = b; }
        @Override
        protected Object doInBackground() throws Exception 
        {
            SwingUtilities.invokeLater(new Runnable() 
            {
                @Override
                public void run() 
                {
                    if (runGUI)
                    {
                        getDialog().setVisible(true);
                    }
                    else
                    {
                        for (int j = 0; j < 30; j++)
                        {
                            System.out.println("j == " + j);
                        }
                    }
                }
            });
            Thread.sleep(20000);
            return null;
        }

        @Override
        protected void done() 
        {
            System.out.println("now in done...");
            JDialog dialog = getDialog();
            // Don't care, dismiss the dialog
            //dialog.setVisible(false);
            dialog.dispose();
        }

    }

    protected JDialog getDialog() 
    {
        if (dialog == null) 
        {
            dialog = new JDialog();
            dialog.setModal(true);
            dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
            dialog.setLayout(new BorderLayout());
            dialog.add(new JLabel("Please wait..."));
            dialog.setSize(200, 200);
            dialog.setLocationRelativeTo(null);
        }
        return dialog;
    }

}

It was working fine when there was only worker running. But, the instant I execute()d another MyWorker, my problem reoccurred. How do I remedy this?

Community
  • 1
  • 1
Mike Warren
  • 3,796
  • 5
  • 47
  • 99
  • 1
    *"the code after my new WaitScreen().execute(); was being executed before it and I had to manually close the dialog to trigger the done()"* - Yes, that would to be expected, because, you SHOULDN'T be updating or modifying the state of the UI from outside the context of the Event Dispatching Thread, that's the whole point of a Swing Worker. What's happening is, when you call `execute` a new `Thread` is create and at some point the future, it will call (indirectly) your `doInBackground` method, in the mean time, all the other threads (like the EDT) are allowed to carry on – MadProgrammer Oct 29 '15 at 05:29
  • Okay, just spotted the `SwingUtilities.invokeLater`, well, that was a complete waste of time creating the `SwingWorker` then – MadProgrammer Oct 29 '15 at 05:31
  • What is alternative to this? – Mike Warren Oct 29 '15 at 05:35
  • Show the dialog immediately after calling `execute` (from within the EDT) – MadProgrammer Oct 29 '15 at 05:36

1 Answers1

1

So, the point of a SwingWorker is to allow you to execute long running/blocking tasks in a background thread, outside of the context of the Event Dispatching Thread, so you don't block the UI.

the code after my new WaitScreen().execute(); was being executed before it and I had to manually close the dialog to trigger the done()

Yes, that would to be expected, that's the whole point of a SwingWorker. What's happening is, when you call execute a new Thread is create and at some point the future, it will call (indirectly) your doInBackground method, in the mean time, all the other threads (like the EDT) are allowed to carry on.

Using SwingUtilities.invokeLater inside the SwingWorker is also some what pointless, the worker is designed to provide functionality to make it easy to send updates back to the EDT.

Okay, in your case, a simple solution would be to call execute first and then immediately display the dialog after in, thereby blocking the code execution at that point, for example...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public class SwingWorkerDialogTest {

    public static void main(String[] args) {
        new SwingWorkerDialogTest();
    }
    private JDialog dialog;

    public SwingWorkerDialogTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {

                } catch (javax.swing.UnsupportedLookAndFeelException ex) {

                }

                MyWorker worker = new MyWorker(true);
                worker.execute();
                new MyWorker(false).makeItSo();
                System.out.println("I'm free !");
            }
        });
    }

    public class MyWorker extends SwingWorker<Object, Object> {

        private boolean runGUI;

        public MyWorker(boolean b) {
            this.runGUI = b;
        }

        public void makeItSo() {
            execute();
            getDialog().setVisible(true);
        }

        @Override
        protected Object doInBackground() throws Exception {
            for (int j = 0; j < 30; j++) {
                System.out.println("j == " + j);
                Thread.sleep(10);
            }
            Thread.sleep(20000);
            return null;
        }

        @Override
        protected void done() {
            System.out.println("now in done...");
            JDialog dialog = getDialog();
            // Don't care, dismiss the dialog
            //dialog.setVisible(false);
            dialog.dispose();
        }

    }

    protected JDialog getDialog() {
        if (dialog == null) {
            dialog = new JDialog();
            dialog.setModal(true);
            dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
            dialog.setLayout(new BorderLayout());
            dialog.add(new JLabel("Please wait..."));
            dialog.setSize(200, 200);
            dialog.setLocationRelativeTo(null);
        }
        return dialog;
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Oh, and I noticed that you put in a `Thread.sleep(10)` after the `System.out.println()` statement. Is this necessary? – Mike Warren Oct 29 '15 at 05:54
  • @MikeWarren No, I just wanted to slow it down a little artificially, to demonstrate that the dialog was, in fact, been displayed and no other code was running – MadProgrammer Oct 29 '15 at 05:57