Question

My current code looks like this:

final String[] value = new String[1];

SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        value[0] = textArea.getText();
    }
});

The use of a final array seems like a bit of a hack. Is there a more elegant solution?

I've done a lot of searching, but I don't seem to be able to find anything to do this, which surprises me. Although I keep coming across SwingWorker, but I'm not sure that's suitable in this case?

I'm assuming that JTextArea.getText() isn't thread-safe.

Thanks.

Was it helpful?

Solution

All problems can be solved by adding another layer of indirection (except when you have too many layers :P).

public class TextSaver implements Runnable
{
    private final JTextArea textArea;
    private final ObjectToSaveText saveHere;

    public TextSaver(JTextArea textArea, ObjectToSaveText saveHere)
    {
        this.textArea = textArea;
        this.saveHere = saveHere;
    }

    @Override
    public void run()
    {
        saveHere.save(textArea.getText());
    }
}

I'm not going to provide the code for ObjectToSaveText, but you get the idea. Then your SwingUtilties call just becomes:

SwingUtilities.invokeAndWait(new TextSaver(textArea, saveHere));

You can retrieve the saved text from your saveHere object.

OTHER TIPS

I find that in 99% of my Swing code that I'm often accessing a JTextArea in response to a user action (the user has typed, clicked a button, closed a window, etc). All of these events are handled through event listeners which are always executed on the EDT.

Can you provide more detail in your use case?

UPDATE BASED ON USE CASE: Can the user change the text after the server has been started? If yes, then you can use the listener style mentioned previously. Make sure to be careful with your concurrency.If the user can't change the text, pass the text to the server thread in response to the button click (which will be on the EDT) and disable the text box.

LAST UPDATE:

If the client connections are persistent and the server continues to send updates you can use the listener model. If not, two copies of the data might be redundant. Either way, I think you'll end up having more threading work (unless you use selectors) on yours hands than worrying about copying one data value.

I think you've got a plethora of info now, good luck.

I encountered the same need, to get swing component value, but from calls of a javascript engine within my application. I slapped together the following utility method.

/**
 * Executes the specified {@link Callable} on the EDT thread. If the calling
 * thread is already the EDT thread, this invocation simply delegates to
 * call(), otherwise the callable is placed in Swing's event dispatch queue
 * and the method waits for the result.
 * <p>
 * @param <V> the result type of the {@code callable}
 * @param callable the callable task
 * @return computed result
 * @throws InterruptedException if we're interrupted while waiting for the
 * event dispatching thread to finish executing doRun.run()
 * @throws ExecutionException if the computation threw an exception
 */
public static <V> V getFromEDT(final Callable<V> callable) throws InterruptedException, ExecutionException {
    final RunnableFuture<V> f = new FutureTask<>(callable);

    if (SwingUtilities.isEventDispatchThread()) {
        f.run();
    } else {
        SwingUtilities.invokeLater(f);
    }

    return f.get();
}

I'm sure you can figure out how to use this, but I like to show how especially brief it is in Java 8:

String text = <String>getFromEDT(() -> textarea.getText());

Edit: changed method to do safe publication

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top