Monday, May 20, 2013

Preform background operation with swing worker

Hi,
I think that one of the suffering things in UI is making a progress bar, you need to update it constantly while the operation is invoked by another thread and calculate the progress of the task.
From java 6.0 we have a new utility that help us with that called SwingWorker.
SwingWorker is an abstract class that you can extend and the abstract method called doInBackground  that allow it to preform background operations. SwingWorker also can update the progress bar with the process method and by firing events for changes in the progress bar.
Let's see an example
In our example, we would like to send mails in the background for large number of users that defined in the Swing application. once the submit button is press the application will send the content of the mail to all the users, let's see the code of the application:

public class Application extends JFrame {
 
  // The UI Components
  private JProgressBar progressBar;
  private JTextArea mails;
  private JTextArea message;
  private JButton sendMails;
  private JLabel status;
 
  public Application(){
   setSize(600, 600);
   message = new JTextArea();
   JPanel rightPanel = new TextAreaPanel(new JLabel("message"), message);
   add(rightPanel, BorderLayout.EAST);
   mails = new JTextArea();
   JPanel leftPanel = new TextAreaPanel(new JLabel("mails"), mails);
   sendMails = new JButton("send it!");
   add(leftPanel, BorderLayout.WEST);
   add(sendMails, BorderLayout.NORTH);
   progressBar = new JProgressBar();
   progressBar.setStringPainted(true);
   add(progressBar, BorderLayout.SOUTH);
   sendMails.addActionListener(new ActionListener() {
  
  @Override
  public void actionPerformed(ActionEvent e) {
   String addresses = mails.getText();
   String[] add = addresses.split("\n");
   SendMailsWorker worker = new SendMailsWorker(message.getText(), add);
   // A property listener used to update the progress bar
      PropertyChangeListener listener = 
                                 new PropertyChangeListener(){
        public void propertyChange(PropertyChangeEvent event) {
          if ("progress".equals(event.getPropertyName())) {
            progressBar.setValue( (Integer)event.getNewValue() );
          }
        }
      };
      worker.addPropertyChangeListener(listener);
   worker.execute();
   
  }
 });
  }
  
 
  // The main method
  public static void main(String[] args){
    SwingUtilities.invokeLater(new Runnable(){
      public void run(){
        Application app = new Application();
        app.setDefaultCloseOperation(EXIT_ON_CLOSE);
        app.pack();
        app.setVisible(true);
      }
    });
    System.out.println("finish main");
  }
}

// and another class for handling the panel is
public class TextAreaPanel extends JPanel{

 public TextAreaPanel(JLabel label, JTextArea text) {
  text.setPreferredSize(new Dimension(120, 120));
  add(label, BorderLayout.WEST);
  add(text, BorderLayout.CENTER);
 }
}

The action of sending the mail should be in the background, therefore, we should create a swing worker that handle the task, let's see it
public class SendMailsWorker extends SwingWorker {

 private final String message;
 private final String[] addresses;

 public SendMailsWorker(String mes, String[] mails) {
  message = mes;
  addresses = mails;
 }

 @Override
 protected Integer doInBackground() throws Exception {
  int matches = 0;
  for (int i = 0, size = addresses.length; i < size; i++) {
   // Update the status: the keep an eye on thing
   publish("send mail to: " + addresses[i]);
   matches += sendMail(message, addresses[i]);
   setProgress((i + 1) * 100 / size);
  }
  return matches;
 }

 private int sendMail(String message2, String string) {
  // sending the mail
  try {
   Thread.sleep(10000);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return 1;
 }

 @Override
 protected void process(List chunks) {
  for (String message : chunks) {
   System.out.println(message);
  }
 }
 
 @Override
 protected void done() {
  System.out.println("finish sending mails");
  super.done();
 }
}
as you can see from the code, the operation of sending the mail is done by the SendMailWorker that extend SwingWorker. the method doInBackground preform the operation and set the progress so than you can write to the console or to any other place the progress of the writing. I think SwingWorker is very nice utility that simplify the ability to preform background operations.

No comments: