Search code examples
javaswingthread-safetyswingworkerconcurrency

Good way to set/refresh information with thread in java swing app


I'm not a Java programmer and I'm not sure if what I'm doing is correct or not, or if exist a better way to do this.

I'm making a swing Java app with multi-threading.
I have many swing component (textfield, texarea, label, list, ...) which are set and refresh with many threads.

For all my component I use something like the code below (it's just a tiny example) for set/refresh it.

Is Main.mainUI.setThumbLbl(image); for set/refresh my component a good way or not ? (I use something like this in other threads for all component)
And is there another better way to do this ?

Main :

public class Main {

  public static MyMainUI mainUI;

  public static void main(String args[]) {
    mainUI = new mainUI();
    java.awt.EventQueue.invokeLater(new Runnable() {

      @Override
      public void run() {        
        mainUI.setVisible(true);
      }
    });
  }
}

Jframe :

public class MyMainUI extends JFrame {

  private JLabel thumbLbl;
  private JButton thumbBtn;

  public MyMainUI(){
    // add thumbLbl, thumBtn
    ...
    thumBtn.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseReleased(MouseEvent evt) {
        new MyThread().start();
      }
    });
  }

  public void setThumbLbl(final Image image) {
    SwingUtilities.invokeLater(new Thread() {

      @Override
      public void run() {
        thumbLbl.setIcon(new ImageIcon(image.getScaledInstance(thumbLblDim.width,
           thumbLblDim.height, Image.SCALE_DEFAULT)));
      }
    });
  }
}

Thread :

public class MyThread extends Thread {
  @Override
  public void run() {
    //Get image from web server
    ...
    Main.mainUI.setThumbLbl(image);
  }
}

NB: I wrote this sample code in a text editor very quickly, maybe there are some mistakes but it's not what I'm asking for ^^.
NB2: Sorry for my bad English.


Solution

  • An example of what I meant is something like this:

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.concurrent.ExecutionException;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    public class Main {
       private static void createAndShowGui() {
          JFrame frame = new JFrame("Main");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.getContentPane().add(new MyMainUI());
          frame.pack();
          frame.setLocationByPlatform(true);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
     }
    
    @SuppressWarnings("serial")
    class MyMainUI extends JPanel {
       public static final String IMG_URL_PATH = "http://duke.kenai.com/Oracle/OracleStrat.png";
       private static final int PREF_W = 900;
       private static final int PREF_H = 650; 
       private JLabel thumbLbl = new JLabel();
       private JButton thumbBtn = new JButton("Get Image");
    
       public MyMainUI() {      
          thumbBtn.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent arg0) {
                thumbBtn.setEnabled(false);
                final ImageDownloader imgDownLoader = new ImageDownloader(IMG_URL_PATH);
                imgDownLoader.execute();
    
                imgDownLoader.addPropertyChangeListener(new ImgDownLoaderListener(imgDownLoader));
             }
          });
    
          JPanel btnPanel = new JPanel();
          btnPanel.add(thumbBtn);
    
          setLayout(new BorderLayout());
          add(new JScrollPane(thumbLbl), BorderLayout.CENTER);
          add(btnPanel, BorderLayout.PAGE_END);
       }
    
       @Override
       public Dimension getPreferredSize() {
          return new Dimension(PREF_W, PREF_H);
       }
    
       private class ImgDownLoaderListener implements PropertyChangeListener {
          ImageDownloader imgDownLoader;
    
          public ImgDownLoaderListener(ImageDownloader imgDownLoader) {
             this.imgDownLoader = imgDownLoader;
          }
    
          @Override
          public void propertyChange(PropertyChangeEvent evt) {
             // swing worker is done
             if (evt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
                thumbBtn.setEnabled(true);
                try {
                   ImageIcon icon = imgDownLoader.get();
                   if (icon != null) {
                      thumbLbl.setIcon(icon);
                   }
                } catch (InterruptedException e) {
                   e.printStackTrace();
                } catch (ExecutionException e) {
                   e.printStackTrace();
                }
             }
          }
       }
    }
    
    
    
    class ImageDownloader extends SwingWorker<ImageIcon, Void> {
       private String imageUrlPath;
    
       public ImageDownloader(String imageUrlPath) {
          this.imageUrlPath = imageUrlPath;
       }
    
       @Override
       protected ImageIcon doInBackground() throws Exception {
          try {
             URL imgUrl = new URL(imageUrlPath);
             BufferedImage img = ImageIO.read(imgUrl);
             return new ImageIcon(img); // return the ImageIcon
          } catch (MalformedURLException e) {
             e.printStackTrace();
          } catch (IOException e) {
             e.printStackTrace();
          }
          return null; // or return null if an error occurs
       }
    
    }
    

    The background worker thread in this example has no knowledge about the structure of the GUI. All it does is download an image -- that's it, and then the GUI which listens for completion with a PropertyChangeListener gets the image by calling get() on the worker, and decides what it wants to do with it.