Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prompt a confirmation dialog box in the middle of non event dispatching thread

I have the following fun which will be executed by non event dispatching thread. In the middle of thread, I want a

  1. A confirmation box pop up. Thread suspend its execution.
  2. User makes a choice.
  3. Thread will get the choice and continue execution.

However, I find out it is not easy to do it in thread safety way, as dialog box should be shown by event dispatching thread. I try

public int fun()
{
    // The following code will be executed by non event dispatching thread.
    final int choice;
    SwingUtilities.invokeAndWait(new Runnable() {

        @Override
        public void run() {
            // Error.
            choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }            
    });
    return choice;
}

Of course this won't work as choice is final, and I cannot assign the returned value from dialog to it.

What is the correct way to achieve the above 3 objectives?

like image 282
Cheok Yan Cheng Avatar asked Jan 20 '11 16:01

Cheok Yan Cheng


3 Answers

Have you tried:

public int fun()
{
    // The following code will be executed by non event dispatching thread.
    final int[] choice = new int[1];
    SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
            // Error.
            choice[0] = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }            
    });
    return choice[0];
}
like image 142
Rekin Avatar answered Nov 01 '22 14:11

Rekin


I am a bit tired of people saying things are one way or another but without actually knowing what they are talking about. I came here wondering which thread JOptionPane should be run on but I get conflicting answers with no real evidence to support either point. Well, I researched it myself and I am well satisfied with this answer so I will share.

A call to one of JOptionPane's showXXXDialog() is BLOCKING until the user selects ok/cancel/etc. In general you do not put such slow blocking insturctions on the Event Dispatch Thread (EDT) as a rule because every single other GUI component will freeze. So, a gut instinct to not put it on the EDT is good, but it is also wrong. The reason is as stated by some others, the method creates GUI components and this should always be done on the EDT. But what about the blocking? You will notice that even if you do run it on the EDT, it works fine. The reason is found inside the source code. The JOptionPane class creates a Dialog object and then calls show() followed by dispose(), the first of which is what blocks the thread. If you read the comments (or javadoc), you will see that it says this about the method:

If the dialog is modal and is not already visible, this call will not return until the dialog is hidden by calling hide or dispose. It is permissible to show modal dialogs from the event dispatching thread because the toolkit will ensure that another event pump runs while the one which invoked this method is blocked.

So, it is perfectly safe to run JOptionPane on the EDT despite it blocking. Obviously, it is safe to call Dialog's show() method off the EDT but the same is not true for JOptionPane because its methods are creating GUI components, adding listeners, accessing other containers when modal and blocking input to them, etc. You do not want all of this done off the EDT because it is not thread-safe and there could be problems. Admittedly, I have never seen problems when using JOptionPane off the EDT and so the chances seem low, but they are most certainly possible. Passing in a null for the container of the dialog and only giving immutable objects (like Strings) as arguments to the fields will significantly reduce (maybe even eliminate as far as I know) the chance of something bad happening because all relevant GUI components are made and accessed within the same thread while they are not visible. But, you should just be safe and put it on the EDT. It is not that difficult to call SwingUtilities.invokeAndWait().

like image 24
David Avatar answered Nov 01 '22 12:11

David


public int fun() throws InterruptedException, InvocationTargetException {
    // The following code will be executed by non event dispatching thread.
    ChoiceRunnable runabble = new ChoiceRunnable();
    SwingUtilities.invokeAndWait(runabble);

    return runabble.choice;
  }

  class ChoiceRunnable implements Runnable {
    private int choice;

    public void run() {
      choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
    }
  }
like image 39
lweller Avatar answered Nov 01 '22 14:11

lweller