I am trying to make an application in Java. To make Swing work correctly, I did this:
public static void main(String[] array){
String outerInput;
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
// I want this string input.
String input = JOptionPane.showInputDialog(
null,"Stop ?", JOptionPane.QUESTION_MESSAGE);
});
// How can I get this input value in String outerInput?
}
How would I get this input string in my main body?
You can trivially expose it to the outer class by declaring a
String[]
in which the runnable sets the value. But note that you will need some synchronization mechanism to know whether it has been assigned by theRunnable
.You wouldn't. The idea that your "main" would invoke a Swing dialog box and then do something with the results is contrary to the entire idea of a graphical user interface.
In a GUI, you design your program to deal with a series of user-initiated events. Those events may be completely asynchronous, such as the keystrokes, selection, and menu choices of your typical word processor. Or they may be scripted, such as the question-answer format of a "wizard."
Assuming that you want to do something like the latter, you would implement it using the following sequence:
ActionListener
, which decides that it needs more input from the user.ActionListener
, which is executed on the event dispatch thread, is permitted to do anything that it wants to the UI, such as displaying a dialog. That dialog may be modal or non-modal; in one case the output is available to the original listener, in the other you need to write a new listener to take subsequent action.SwingUtilities.invokeLater()
. While you could useinvokeAndWait()
to send results to Swing in the middle of your background operation, that's rarely a good idea. Instead, create a sequence of operations, preferably one that is easily canceled by the user.The "standard" way to initiate operations on a background thread is via SwingWorker. There are alternatives; for example, you could use a
BlockingQueue
to send operations to a single long-running background thread, and useinvokeLater()
to return the results.Regardless, there's one rule that you do not want to break: never, ever, perform a blocking operation on the event dispatch thread. If you do that, then your application is broken.
Right now you have two threads going: the main thread and the EDT (event dispatch thread). I assume you know that
SwingUtilities.invokeLater(runnable)
is running a task on the EDT.To share data between threads, you just need some variable that is in the scope of both threads. The easiest way to accomplish that is to declare a
volatile
data member orAtomicReference
in the class containing the main method.In order to ensure that you read the value after it is returned by the
JOptionPane
, the simplest thing you can do here is to change the invokeLater call to aninvokeAndWait
call. This will cause your main thread to stop executing until what you have put onto the EDT has completed.Ex:
If your main thread is executing some task that shouldn't be stopped while the option pane is present, then in the main thread you can periodically check (i.e., in the outer part of the loop that is running your task) whether or not
mySharedData
has been set. If your task doesn't loop and is instead doing some I/O or waiting, you can make use of Thread.interrupt and checkmySharedData
in the InterruptedExecption handlers.You can use an AtomicReference and invokeAndWait.
The following code will do what you want. I have done something similar except I was launching a
JFileChooser
instead of an input dialog. I found it more convenient than hard coding a bunch of paths into my application or accepting a command line argument, at least for testing purposes. I would like to add that one could modify theprompt()
method to return theFutureTask
instance for added flexibility.You can use
AtomicReference<String>
for passing values between threads in a thread-safe manner.As noted by Hemal, you'll need some synchronization between two threads to make sure it was already executed. For example, you can use CountDownLatch or use SwingUtilities.invokeAndWait (make sure you don't call it from Swing thread!)
Update: here is the complete example using AtomicReference and CountDownLatch
Also read this answer about general design of GUI (and Swing) applications.