Question

Such a piece of code:

private void log(String message) {
    LogBox.append(message + "\n");
}

private void log(Exception e) {
    log(e.getMessage());
}

private void ConvertButtonActionPerformed(java.awt.event.ActionEvent evt) {
    String url = UrlBox.getText();
    if (url.isEmpty()) {
        log("Empty URL");
        return;
    }
    LogBox.setText("");
    try {
        log("URL "+url+" accepted. Trying to download...");
        String content = URLConnectionReader.getText(url);
        log("Downloaded. Parsing the content...");
        //
    } catch (Exception e) {
        log(e);
    }
}

should output each message to the LogBox (JTextArea) immediately after each log call, but outputs URL ... accepted only when URLConnectionReader.getText(url) finishes.

There were several ways do do an immediate output:

  • Application.DoEvents in Visual Basic 6 and .NET
  • Application.ProcessMessages in Delphi

Is there some simple way to do an immediate output? I was studying questions about the DoEvents and how to do this in Java, but I think that starting to learn Java from multi-threading isn't a right approach.

Was it helpful?

Solution

Create a SwingWorker to do the download: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html


The role of an ActionListener is just that: to listen for user action, and initiate a response to that action by the program.

Sometimes, the program's response is very quick, and only involves the GUI. For example, in a calculator app, you could have a listener attached to an "equals" button that calculates the result of the current expression and writes it to a textbox. This can all be done within the listener method (although you might want to separate behavior for testing).

If the response to an user action is to initiate some long-running process, like downloading and parsing the file, then you don't want to do this within the listener body, because it will freeze the UI. Instead, gather any information (in your case, the URL value) from within the listener, and spin up a SwingWorker to handle the program's response.

In my comment, I suggested moving everything after the getText() into a SwingWorker. This is because, to me, the response is "download a file if you have a valid URL, and log the progress." And as I see it, testing for an empty string is part of that response. If you want to leave the empty-string test inside the listener, that's fine, but imo it's less testable.

You must leave the call to getText() inside the body of the listener, because you are only allowed to access Swing components from the event dispatch thread. If you moved that call into the worker, then it might access the textbox at the same time the textbox is updating itself, resulting in corrupt data.

OTHER TIPS

Read up on Concurrency.

You should probably use a SwingWorker for the long running task, then you can publish results to the GUI as they become available.

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