您的位置:首页 > 其它

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

2015-11-16 14:43 288 查看
为什么要使用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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: