Search code examples
javamultithreadingswingjtextarea

JTextArea not updating in real time despite attempt at correct threading


Here is another one of those 'how do I get a JTextArea to update in real time' questions. I've read every one of the existing posts, and I think that I am threading this correctly. However, I'm still having that same problem - my JTextArea doesn't update until the loop has finished and then it updats in one big blast. Can somebody see where I am doing this incorrectly? I can't see it. Thanks!

EDIT: switch the text area updating the called method. But I still have the same result!

private void saveImagesToDisk() {

    textArea.append("\nPreparing to save photos to photos directory\n\n");

    for (MediaFeedData mfd : imagesMetaDataList) {
        try {
            String imageURL = mfd.getImages().getLowResolution().getImageUrl();
            final String filename = mfd.getId(); // just name the file using the image id from instagram
            System.out.println(filename);

            SaveImageFromUrl.saveImage(imageURL, filename, textArea);

        } catch (IOException e) {
            e.printStackTrace();

        }
    }

}

Then, on in the save method, I have this:

public class SaveImageFromUrl {

public static Boolean saveImage(final String imageUrl, String destinationFile, final JTextArea textArea) throws IOException {

    String directoryName = "photos";
    if (!makePhotosDirectory(directoryName, textArea)) {return false;}

    File file = new File(directoryName, destinationFile);
    URL url = new URL(imageUrl);
    InputStream is = url.openStream();
    OutputStream os = new FileOutputStream(file);

    byte[] b = new byte[2048];
    int length;

    while ((length = is.read(b)) != -1) {
        os.write(b, 0, length);
    }

    is.close();
    os.close();

    new Thread() {
        public void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    textArea.append(imageUrl + " written to photos directory\n");
                }
            });
        }       
    }.start();


    return true;
}

Solution

  • Both the for loop and the code in the method below need to be called in a background thread, and this you're not doing.

    if (SaveImageFromUrl.saveImage(imageURL, filename, textArea)) {
    

    Just using a background thread will not help if the time-intensive code is not the code that is called in the background.

    Consider using a SwingWorker, something like,...

       private void saveImagesToDisk() {
          textArea.append("\nPreparing to save photos to photos directory\n\n");
    
          final SwingWorker<Void, String> imageWorker = new SwingWorker<Void, String>() {
             @Override
             protected Void doInBackground() throws Exception {
    
                for (MediaFeedData mfd : imagesMetaDataList) {
                   final String imageURL = mfd.getImages().getLowResolution().getImageUrl();
                   final String filename = mfd.getId();
                   System.out.println(filename);
                   String textToPublish = "";
                   if (SaveImageFromUrl.saveImage(imageURL, filename, textArea)) {
                      textToPublish = filename + " written to photos directory\n";
                   } else {
                      textToPublish = filename + " not saved!\n";
                   }
    
                   // publish String so it can be used in the process method
                   publish(textToPublish);
                }
                return null;
             }
    
             @Override
             protected void process(List<String> chunks) {
                // Strings sent to the EDT by the publish method
                // This called on the Swing event thread.
                for (String chunk : chunks) {
                   textArea.append(chunk);
                }
             }
          };
    
          imageWorker.addPropertyChangeListener(new PropertyChangeListener() {
    
             @Override
             public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                   try {
                      // need to call this on the event thread
                      // to catch any exceptions that have occurred int he Swing Worker
                      imageWorker.get();
                   } catch (InterruptedException e) {
                      e.printStackTrace();
                   } catch (ExecutionException e) {
                      // TODO catch exceptions buried in this guy
                      e.printStackTrace();
                   }
                }
             }
          });
    
          // run our SwingWorker
          imageWorker.execute();
       }
    

    For more details, please read Concurrency in Swing.