使用泛型SwingWorker与EDT事件分发线程保持通讯

为什么要使用SwingWorker

在swing开发中,如果一个应用程序,执行一些任务,需要大量的时间来完成,比如下载一个大文件或执行一个复杂的数据库查询。

我们假设这些任务是由用户使用一个按钮触发的。在单线程应用程序,用户单击按钮,进入计算的过程,然后等待任务完成之前,所有的事件都在主线程EDT线程进行。

但如果某些任务耗时很长,用户将甚至不能在中途取消任务,应用程序必须响应只有当长任务完成。不幸的是,许多应用程序显着这样的行为和用户感到沮丧,程序仿佛卡死一样。

多线程可以解决这个问题。它使应用程序能够在不同的线程上执行长任务,但多线程带来一个问题,如果需要实时和主线程EDT进行数据交换,该怎么办?我们知道所有的Swing对象都只有一个线程处理,EDT事件调度线程,这导致一个问题:我们不能在EDT事件分派线程以外的其他线程共享对象的对象。

 

SwingWorker

SwingWorker是一个抽象类,java将它包装好,供方便调用,下面的例子使用字符串对象来通知应用程序。

提供了两个泛型参数。第一个代表返回的对象类型。另一个代表了通知(更新)应用程序的信息的类型,并在下面的例子中高亮显示。

public class MyBlankWorker extends SwingWorker<Integer, String> {

  @Override
  protected Integer doInBackground() throws Exception {
    // Start
    publish("Start");
    setProgress(1);
    
    // More work was done
    publish("More work was done");
    setProgress(10);

    // Complete
    publish("Complete");
    setProgress(100);
    return 1;
  }
  
  @Override
  protected void process(List< String> chunks) {
    // Messages received from the doInBackground() (when invoking the publish() method)
  }
}

通过setprogress() 设置0和100之间的整数。doinbackground() 用于漫长任务执行。此方法不是由事件调度线程调用的,而是由另一个线程(称为工作线程)。我们可以用publish()方法和/或setprogress()更新进度。调用两个方法都会对事件调度线程的创建新任务,它是工作线程和事件调度线程的线程之间的单向桥。

doinbackground()中调用publish()方法和/或setprogress()必须防止大量的任务发送给事件调度线程,引发洪水事件

注意:publish()从工作线程调用,而process()由事件调度线程EDT调用

举例:

输入:

 publish("a");
 publish("b", "c");
 publish("d", "e", "f");

结果就是:

 process("a", "b", "c", "d", "e", "f")


最后是一个文件全文检索的异步查询例子,查询某目录下所有的txt文件

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTextArea;
import javax.swing.SwingWorker;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.StringUtils;

/**
 * Searches the text files under the given directory and counts the number of instances a given word is found in these
 * file.
 * 
 * @author Albert Attard
 */
public class SearchForWordWorker extends SwingWorker<Integer, String> {

  private static void failIfInterrupted() throws InterruptedException {
    if (Thread.currentThread().isInterrupted()) {
      throw new InterruptedException("Interrupted while searching files");
    }
  }

  /** The word that is searched */
  private final String word;

  /** The directory under which the search occurs. All text files found under the given directory are searched. */
  private final File directory;

  /** The text area where messages are written. */
  private final JTextArea messagesTextArea;

  /**
   * Creates an instance of the worker
   * 
   * @param word
   *          The word to search
   * @param directory
   *          the directory under which the search will occur. All text files found under the given directory are
   *          searched
   * @param messagesTextArea
   *          The text area where messages are written
   */
  public SearchForWordWorker(final String word, final File directory, final JTextArea messagesTextArea) {
    this.word = word;
    this.directory = directory;
    this.messagesTextArea = messagesTextArea;
  }

  @Override
  protected Integer doInBackground() throws Exception {
    // The number of instances the word is found
    int matches = 0;

    /*
     * List all text files under the given directory using the Apache IO library. This process cannot be interrupted
     * (stopped through cancellation). That is why we are checking right after the process whether it was interrupted or
     * not.
     */
    publish("Listing all text files under the directory: " + directory);
    final List<File> textFiles = new ArrayList<>(FileUtils.listFiles(directory, new SuffixFileFilter(".txt"),
        TrueFileFilter.TRUE));
    SearchForWordWorker.failIfInterrupted();
    publish("Found " + textFiles.size() + " text files under the directory: " + directory);

    for (int i = 0, size = textFiles.size(); i < size; i++) {
      /*
       * In order to respond to the cancellations, we need to check whether this thread (the worker thread) was
       * interrupted or not. If the thread was interrupted, then we simply throw an InterruptedException to indicate
       * that the worker thread was cancelled.
       */
      SearchForWordWorker.failIfInterrupted();

      // Update the status and indicate which file is being searched.
      final File file = textFiles.get(i);
      publish("Searching file: " + file);

      /*
       * Read the file content into a string, and count the matches using the Apache common IO and Lang libraries
       * respectively.
       */
      final String text = FileUtils.readFileToString(file);
      matches += StringUtils.countMatches(text, word);

      Thread.sleep(20);
      // Update the progress
      setProgress((i + 1) * 100 / size);
    }

    // Return the number of matches found
    return matches;
  }

  @Override
  protected void process(final List<String> chunks) {
    // Updates the messages text area
    for (final String string : chunks) {
      messagesTextArea.append(string);
      messagesTextArea.append("\n");
    }
  }
}

JFrame UI类如下

package com.javacreed.examples.swing.worker.part3;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingWorker.StateValue;

public class Application extends JFrame {

  /**  */
  private static final long serialVersionUID = -8668818312732181049L;

  private Action searchCancelAction;
  private Action browseAction;

  private JTextField wordTextField;
  private JTextField directoryPathTextField;
  private JTextArea messagesTextArea;
  private JProgressBar searchProgressBar;

  private SearchForWordWorker searchWorker;

  public Application() {
    initActions();
    initComponents();
  }

  private void cancel() {
    searchWorker.cancel(true);
  }

  private void initActions() {
    browseAction = new AbstractAction("Browse") {

      private static final long serialVersionUID = 4669650683189592364L;

      @Override
      public void actionPerformed(final ActionEvent e) {
        final File dir = new File(directoryPathTextField.getText()).getAbsoluteFile();
        final JFileChooser fileChooser = new JFileChooser(dir.getParentFile());
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        final int option = fileChooser.showOpenDialog(Application.this);
        if (option == JFileChooser.APPROVE_OPTION) {
          final File selected = fileChooser.getSelectedFile();
          directoryPathTextField.setText(selected.getAbsolutePath());
        }
      }
    };

    searchCancelAction = new AbstractAction("Search") {

      private static final long serialVersionUID = 4669650683189592364L;

      @Override
      public void actionPerformed(final ActionEvent e) {
        if (searchWorker == null) {
          search();
        } else {
          cancel();
        }
      }
    };
  }

  private void initComponents() {
    setLayout(new GridBagLayout());

    GridBagConstraints constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 0;
    constraints.insets = new Insets(2, 2, 2, 2);
    add(new JLabel("Word: "), constraints);

    wordTextField = new JTextField();
    wordTextField.setText("Hello");
    constraints = new GridBagConstraints();
    constraints.gridx = 1;
    constraints.gridy = 0;
    constraints.gridwidth = 2;
    constraints.insets = new Insets(2, 2, 2, 2);
    constraints.weightx = 1;
    constraints.fill = GridBagConstraints.BOTH;
    add(wordTextField, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 1;
    constraints.insets = new Insets(2, 2, 2, 2);
    add(new JLabel("Path: "), constraints);

    directoryPathTextField = new JTextField();
    directoryPathTextField.setText("C:\\Users\\Albert\\Work\\JavaCreed\\examples");
    constraints = new GridBagConstraints();
    constraints.gridx = 1;
    constraints.gridy = 1;
    constraints.gridwidth = 1;
    constraints.insets = new Insets(2, 2, 2, 2);
    constraints.weightx = 1;
    constraints.fill = GridBagConstraints.BOTH;
    add(directoryPathTextField, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 2;
    constraints.gridy = 1;
    constraints.insets = new Insets(2, 2, 2, 2);
    add(new JButton(browseAction), constraints);

    messagesTextArea = new JTextArea();
    messagesTextArea.setEditable(false);
    constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 2;
    constraints.gridwidth = 3;
    constraints.insets = new Insets(2, 2, 2, 2);
    constraints.weightx = 1;
    constraints.weighty = 1;
    constraints.fill = GridBagConstraints.BOTH;
    add(new JScrollPane(messagesTextArea), constraints);

    searchProgressBar = new JProgressBar();
    searchProgressBar.setStringPainted(true);
    searchProgressBar.setVisible(false);
    constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 3;
    constraints.gridwidth = 2;
    constraints.insets = new Insets(2, 2, 2, 2);
    constraints.weightx = 1;
    constraints.fill = GridBagConstraints.BOTH;
    add(searchProgressBar, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.insets = new Insets(2, 2, 2, 2);
    constraints.weightx = 0;
    add(new JButton(searchCancelAction), constraints);
  }

  private void search() {
    final String word = wordTextField.getText();
    final File directory = new File(directoryPathTextField.getText());
    messagesTextArea.setText("Searching for word '" + word + "' in text files under: " + directory.getAbsolutePath()
        + "\n");
    searchWorker = new SearchForWordWorker(word, directory, messagesTextArea);
    searchWorker.addPropertyChangeListener(new PropertyChangeListener() {
      @Override
      public void propertyChange(final PropertyChangeEvent event) {
        switch (event.getPropertyName()) {
        case "progress":
          searchProgressBar.setIndeterminate(false);
          searchProgressBar.setValue((Integer) event.getNewValue());
          break;
        case "state":
          switch ((StateValue) event.getNewValue()) {
          case DONE:
            searchProgressBar.setVisible(false);
            searchCancelAction.putValue(Action.NAME, "Search");
            searchWorker = null;
            break;
          case STARTED:
          case PENDING:
            searchCancelAction.putValue(Action.NAME, "Cancel");
            searchProgressBar.setVisible(true);
            searchProgressBar.setIndeterminate(true);
            break;
          }
          break;
        }
      }
    });
    searchWorker.execute();
  }
}
View Code

入口:

 

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class Main {
  public static void main(final String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        final Application frame = new Application();
        frame.setTitle("Swing Worker Demo");
        frame.setSize(600, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
      }
    });
  }
}

 

引用:http://www.javacreed.com/swing-worker-example/

 

posted @ 2015-11-16 14:43  昕友软件开发  阅读(613)  评论(0编辑  收藏  举报
欢迎访问我的开源项目:xyIM企业即时通讯