演示程序介绍
1.从其他线程访问UI组件及其事件处理器会导致界面更新和绘制错误。
2.在EDT上执行耗时任务会使程序失去响应,这会使GUI事件阻塞在队列中得不到处理。
3.应使用独立的任务线程来执行耗时计算或输入输出密集型任务,比如同数据库通信、访问网站资源、读写大树据量的文件。
public class MainFrame extendsjavax.swing.JFrame {
}
}
SwingWorker基础
public abstract class SwingWorker<T,V> extends Objectimplements RunnableFuture
protected T doInBackground() throws Exception
protected void done()
实现简单的ImageRetriever
当点击列表所略图时,事件处理器创建了一个ImageRetriever实例并执行之。ImageRetriever下载选中的图片并在列表下面展示它。当实现SwingWorker子类,须指定doInBackground和get方法返回值的类型。因为ImageRetriever并不生成中间结果,它使用特殊类型Void作为中间类型,ImageRetriever的任务的结果是一图片,因此使用Icon类型作为doInBackground和get方法的返回类型,下面代码显示了ImageRetriever的大部分实现:
public class ImageRetriever extends SwingWorker<Icon, Void> {
}
public class ImageRetriever extends SwingWorker<Icon, Void>
doInBackground方法从类构造函数中提供的URL中获得图象并产生一个Icon结果:
@Overrideprotected Icon doInBackground() throws Exception {
}
@Override
protected void done() {
}
reader.addIIOReadProgressListener(new IIOReadProgressListener() { ... public void imageProgress(ImageReader source, float percentageDone) { setProgress((int) percentageDone); } public void imageComplete(ImageReader source) { setProgress(100); } });
图7.SwingWorker线程在任务完成后更新进度条和标签图标
使用简单ImageRetriever
private void listImagesValueChanged(ListSelectionEvent evt) { ... ImageInfo info = (ImageInfo) listImages.getSelectedValue(); String id = info.getId(); String server = info.getServer(); String secret = info.getSecret(); // No need to search an invalid thumbnail image if (id == null || server == null || secret == null) { return; } String strImageUrl = String.format(IMAGE_URL_FORMAT, server, id, secret); retrieveImage(strImageUrl); ... } private void retrieveImage(String imageUrl) { // SwingWorker objects can't be reused, so // create a new one as needed. ImageRetriever imgRetriever = new ImageRetriever(lblImage, imageUrl); progressSelectedImage.setValue(0); // Listen for changes in the "progress" property. // You can reuse the listener even though the worker thread // will be a new SwingWorker. imgRetriever.addPropertyChangeListener(listenerSelectedImage); progressSelectedImage.setIndeterminate(true); // Tell the worker thread to begin with this asynchronous method. imgRetriever.execute(); // This event thread continues immediately here without blocking. }
/** * ProgressListener listens to "progress" property * changes in the SwingWorkers that search and load * images. */ class ProgressListener implements PropertyChangeListener { // Prevent creation without providing a progress bar. private ProgressListener() {} ProgressListener(JProgressBar progressBar) { this.progressBar = progressBar; this.progressBar.setValue(0); } public void propertyChange(PropertyChangeEvent evt) { String strPropertyName = evt.getPropertyName(); if ("progress".equals(strPropertyName)) { progressBar.setIndeterminate(false); int progress = (Integer)evt.getNewValue(); progressBar.setValue(progress); } } private JProgressBar progressBar; }
实现ImageSearcher
实现SwingWorker子类时,在类声明处要指定最终和中间结果的类型,ImageSearcher搜索并下载匹配的缩略图。由于该类在任务结束时产生匹配图像的列表,所以该类使用List作为类的类型参数,为表明它中间发布的数据是匹配图片,它还使用ImageInfo作为类型参数,ImageSearcher的定义如下:
public class ImageSearcherextends SwingWorker<List<ImageInfo>, ImageInfo> {
}
@Override
protected List<ImageInfo> doInBackground() {
}
private void retrieveAndProcessThumbn
}
/**
@Override
protected void process(List<ImageInfo> infoList) {
}
注意process方法的参数,它并没有使用单个ImagInfo对象,而是这种对象的一个列表。原因是publish方法能够以批模式来调用process方法,就是说,每个publish调用并不总是产生相应的process调用。如果可能,publish方法会收集对象并以对象的列表为参数调用process方法。实现process方法要以对象列表的方式处理,就像下面的代码:
@Overrideprotected void process(List<ImageInfo> infoList) {
}
doInBackground方法调用retrieveAndProcessThumbn ails方法,该方法循环列表的图像数据并获取这些图像的缩略图。然而当任务线程正在执行循环时,用户可以启动新的查询。因此这儿也需要检查取消请求:
private void retrieveAndProcessThumbnails(List<ImageInfo> infoList) {
}
该类处理缩略图的同时就发布它们,其结果是在EDT上运行process方法。如果用户请求取消,或者启动新的搜索,可以通过在process方法内检查来避免这种情况的发生。
protected void process(List<ImageInfo> infoList) {
}
最后,一旦任务线程完成,它还有一个机会更新GUI的模型,这就是在done方法中。因此在这儿也要检查取消请求:
@Overrideprotected void done() {
}
ImageSearcher类是一个更为完整的SwingWorker的例子,它比ImageRetriever做的更多,ImageSearcher类往GUI上发布中间数据,并处理任务取消请求。两个类都在后台执行任务,并通过事件处理器跟踪进度。 使用ImageSearcher类 演示程序提供一个搜索输入栏。当用户输入图片查询条件时,MainFrame类创建一个ImageSearcher实例,输入一个查询条件并产生一个键盘事件,输入栏的键盘事件激活searchImage方法,该方法实例化一个ImageSearcher对象并执行之:
private void searchImages(String strSearchText, int page) {
}
注意代码在ImageSearcher构造函数中提供一个listModel作为参数,这个模型允许任务线程能直接访问更新列表内容。也可以向任务对象添加一个属性改变处理器。上文添加了一个属性改变处理器来更新进度条。除此外还需要添加一个处理器以响应任务线程的状态变化,特别是要侦听DONE状态并使用前文提到的get方法获取任务结果。 一旦执行任务线程,就会搜索并下载缩略图。该程序向任务对象提供了一个列表模型,因此它会直接更新列表。另外,ImageSearcher类提供了中间数据,因此可以在下载图片的同时更新JList组件。其运行的直接效果就是改善了程序的性能。如下图8所示,搜索结果是列表中显示的小图片: 图8. 缩略图是任务线程发布的中间数据 你可以通过调用其cancel方法取消SwingWorker线程。用户可以在当前搜索正在进行时输入新的搜索条件并提交来取消当前图像搜索。搜索输入栏的事件处理器检查现有线程是否正在运行,如果正在运行则调用cancel来取消之:
private void searchImages(String strSearchText, int page) {
}
当调用cancel方法时,代码产生新的任务实例,每一个新的搜索需要自己的任务实例。 总结 所有的GUI事件和交互都运行在EDT上,在EDT上运行耗时或者I/O密集型处理会导致界面变得缓慢失去响应,为改善这种状况应使用Java SE 6中提供的SwingWorker类将这些任务转移到任务线程中。 使用SwingWorker,你可以执行相同的任务而不会延迟EDT运行,会提高程序的性能。并且,任务线程可以安全同界面组件交互,因为有回调方法可以在EDT上运行,允许任务运行和完成时更新GUI组件。
来源:http://blog.sina.com.cn/s/blog_4b6047bc010007tt.html