Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 443|回复: 0

[Swing学习]学习使用SwingWorker(2)

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-11-16 00:01:39 | 显示全部楼层 |阅读模式
    本节简要介绍SwingWorker的功能。SwingWorker的定义如下: public abstract class SwingWorker<T,V> extends Object implements RunnableFuture

        SwingWorker是抽象类,因此必须继承它才能执行所需的特定任务。注意该类有两个类型参数:T及V。T是doInBackground和get方法的返回类型,V是publish和process方法要处理的数据类型。后文将作详细解释。     该类实现了java.util.concurrent.RunnableFuture接口。RunnableFuture接口是Runnable和 Future两个接口的简单封装。由于SwingWorker实现了Runnable接口,因此SwingWorker有一个run方法。 Runnable对象一般作为线程的一部分执行,当Thread对象启动时,它激活Runnable对象的run方法。由于SwingWorker实现了 Future接口,因此SwingWorker产生类型为T的结果值并提供同线程交互的方法。SwingWorker实现以下接口方法:  * boolean cancel(boolean mayInterruptIfRunning)
    * T get()
    * T get(long timeout, TimeUnit unit)
    * boolean isCancelled()
    * boolean isDone()  SwingWorker实现了所有的接口方法,实际上你仅需要实现以下SwingWorker的抽象方法: protected T doInBackground() throws Exception     doInBackground方法作为任务线程的一部分执行,它负责完成线程的基本任务,并以返回值来作为线程的执行结果。继承类须覆盖该方法并确保包含或代理任务线程的基本任务。不要直接调用该方法,应使用任务对象的execute方法来调度执行。    在获得执行结果后应使用SwingWorker的get方法获取doInBackground方法的结果。可以在EDT上调用get方法,但该方法将一直处于阻塞状态,直到任务线程完成。最好只有在知道结果时才调用get方法,这样用户便不用等待。为防止阻塞,可以使用isDone方法来检验 doInBackground是否完成。另外调用方法get(long timeout, TimeUnit unit)将会一直阻塞直到任务线程结束或超时。获取任务结果的最好地方是在done方法内: protected void done()      在doInBackground方法完成之后,SwingWorker调用done方法。如果任务需要在完成后使用线程结果更新GUI组件或者做些清理工作,可覆盖done方法来完成它们。这儿是调用get方法的最好地方,因为此时已知道线程任务完成了,SwingWorker在EDT上激活done方法,因此可以在此方法内安全地和任何GUI组件交互。  没必要等到线程完成就可以获得中间结果。中间结果是任务线程在产生最后结果之前就能产生的数据。当任务线程执行时,它可以发布类型为V的中间结果,覆盖 process方法来处理中间结果。后文还将提供这些方法的更多详细信息。当属性改变时,SwingWorker实例能通知处理器,SwingWorker有两个重要的属性:状态和进程。任务线程有几种状态,以下面SwingWorker.StateValue枚举值来表示:  * PENDING
    * STARTED
    * DONE    任务线程一创建就处于PENDING状态,当doInBackground方法开始时,任务线程就进入STARTED状态,当 doInBackground方法完成后,任务线程就处于DONE状态,随着线程进入各个阶段,SwingWorker超类自动设置这些状态值。你可以添加处理器,当这些属性发生变化来接收通知。  最后,任务对象有一个进度属性,随着任务进展时,可以将这个属性从0更新到100标识任务进度,当该属性发生变化时,任务通知处理器进行处理。 实现简单的ImageRetriever     当点击列表缩略图时,事件处理器创建了一个ImageRetriever实例并执行之。ImageRetriever下载选中的图片并在列表下面展示它。当实现SwingWorker子类,须指定doInBackground和get方法返回值的类型。因为ImageRetriever并不生成中间结果,它使用特殊类型Void作为中间类型,ImageRetriever的任务的结果是一图片,因此使用Icon类型作为doInBackground和get方法的返回类型,下面代码显示了ImageRetriever的大部分实现:
    1. public class ImageRetriever extends SwingWorker< Icon, Void> {
    2.     private ImageRetriever() {}   
    3.     public ImageRetriever(JLabel lblImage, String strImageUrl) {
    4.         this.strImageUrl = strImageUrl;
    5.         this.lblImage = lblImage;
    6.     }   
    7.     @Override
    8.     protected Icon doInBackground() throws Exception {
    9.         Icon icon = retrieveImage(strImageUrl);
    10.         return icon;
    11.     }   
    12.     private Icon retrieveImage(String strImageUrl)
    13.             throws MalformedURLException, IOException {      
    14.         InputStream is = null;
    15.         URL imgUrl = null;
    16.         imgUrl = new URL(strImageUrl);
    17.         is = imgUrl.openStream();
    18.         ImageInputStream iis = ImageIO.createImageInputStream(is);
    19.         Iterator< ImageReader> it =ImageIO.getImageReadersBySuffix("jpg");      
    20.         ImageReader reader = it.next();
    21.         reader.setInput(iis);     
    22.                    ...
    23.         Image image = reader.read(0);
    24.         Icon icon = new ImageIcon(image);
    25.         return icon;
    26.     }   
    27.     @Override
    28.     protected void done() {
    29.         Icon icon = null;
    30.         String text = null;
    31.         try {
    32.             icon = get();
    33.         } catch (Exception ignore) {
    34.             ignore.printStackTrace();
    35.             text = "Image unavailable";
    36.         }
    37.         lblImage.setIcon(icon);
    38.         lblImage.setText(text);
    39.     }   
    40.     private String strImageUrl;
    41.     private JLabel lblImage;
    42. }
    复制代码
    因为ImageRetriever类下载图象并把它以label图标的方式展现,因此为了方便在其构造函数中要提供一个JLabel实例和图象URL。 ImageRetriever需要图象URL来下载图象,需要一个JLabel实例来展现下载的图象,如果使用内部类实现任务线程 ImageRetriever,由于可以直接访问这些信息,你甚至不需要在构造函数中提供这些信息(图象URL以及展现图象的JLabel实例)。这样做这些信息不会在ImageRetriever实例之间共享,所以更容易帮助程序实现线程安全。    注意ImageRetriever指定Icon作为doInBackground和get方法的返回类型,因为并不产生任何中间数据,所以指定Void类型作为中间结果类型。 public class ImageRetriever extends SwingWorker<Icon, Void>     在该实现中,doInBackground方法必须遵循类协议,返回一个Icon类型的对象。通过在类定义中指定Icon类型,编译器强制 doInBackground和get方法要返回Icon类型的值。不要覆盖get方法,因为该方法在SwingWorker中是一个final方法。  doInBackground方法从类构造函数中提供的URL中获得图象并产生一个Icon结果: @Override protected Icon doInBackground() throws Exception {
       Icon icon = retrieveImage(strImageUrl);
       return icon;
    }  当doInBackground方法完成后,SwingWorker在EDT上调用done方法,不要直接调用这个方法,因为SwingWorker超类会调用这个方法。done方法获得Icon结果并把它放到标签label上。在本例中,lblImage引用通过ImageRetriever构造函数传入进来。 @Override
    protected void done() {
       ...
       icon = get();
       ...
       lblImage.setIcon(icon);
       ...
    }    progress属性值范围是从0到100,当你在任务实例内处理这些信息时,你可以调用setProgress方法来更新这个属性。当ImageRetriever通过ImageIO API下载图象时,它调用setProgress方法来更新其progress属性。下面的代码展示如何使用IIOReadProgressListener实例来跟踪ImageReader对象任务、下载图象并更新progress属性:

    1. reader.addIIOReadProgressListener(new IIOReadProgressListener() {
    2.   public void sequenceStarted(ImageReader source, int minIndex) {
    3.   }
    4.             
    5.   public void sequenceComplete(ImageReader source) {
    6.   }
    7.             
    8.   public void imageStarted(ImageReader source, int imageIndex) {
    9.   }
    10.            
    11.   public void imageProgress(ImageReader source, float percentageDone) {
    12.      setProgress((int) percentageDone);
    13.             
    14.   }
    15.             
    16.   public void imageComplete(ImageReader source) {
    17.      setProgress(100);
    18.   }
    19.            
    20.   public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) {
    21.    }
    22.             
    23. public void thumbnailProgress(ImageReader source, float percentageDone) {
    24.    }
    25.             
    26.    public void thumbnailComplete(ImageReader source) {
    27.   }
    28.             
    29.   public void readAborted(ImageReader source) {
    30.    }
    31.          
    32. });
    复制代码
      当任务属性发生变化时,它通知处理器对象。在前面例子代码中,当ImageRetriever类从ImageIO API那儿收到更新信息时调用setProgress方法并使用这些信息来设置自己的进度属性,作为结果,属性变化处理器知道了当前下载了多少图象数据。    ImageRetriever演示了一个SwingWorker的简单实现,在简单实现中,只需覆盖方法doInBackground就行。然而,因为只有doInBackground方法完成之后,任务最后的结果才能获得,所以你也应该覆盖done方法。    SwingWorker在doInBackground方法完成之后激活done方法,所以应该从done方法中获得任务结果,并且应该使用get方法获得任务结果。通过在ImageRetriever构造函数提供这些UI组件,保证了能在任务线程的done方法中直接更新这些组件。前面的例子显示了如何使用从Flickr站点获取的图象设置标签label的图标。参考ImageRetriever的完整实现了解如何从Flickr网站获得图像。 使用简单ImageRetriever    现在已经创建了简单的SwingWorker实现ImageRetriever,如何使用它呢?首先,要实例化,接着调用它的execute方法。当用户从JList中选择一个缩略图,演示应用程序的MainFrame类就使用ImageRetriever任务线程。当用户点击一个缩略图,列表选择发生变化,产生一个列表选择事件,listImageValueChanged方法是事件处理器。listImageValueChanged方法获得选中的列表项,并生成相应缩略图片对应的图象的Flikr服务URL字符串,事件处理器接着调用retrieveImage方法,retrieveImage方法包含生成和使用ImageRetriever任务线程的重要代码。

    1. private void listImagesValueChanged(javax.swing.event.ListSelectionEvent evt) {
    2.         // don"t do anything if this is just a changing list
    3.         // being populated by thumbnail images
    4.         if (evt.getValueIsAdjusting() || listImages.isSelectionEmpty()) {
    5.             return;
    6.         }
    7.         // handle selection made by the user
    8.         int selectedIndex = listImages.getSelectedIndex();
    9.         if (selectedIndex >= 0) {
    10.             ImageInfo info = (ImageInfo) listImages.getSelectedValue();
    11.             String id = info.getId();
    12.             String server = info.getServer();
    13.             String secret = info.getSecret();
    14.             // no need to search an invalid thumbnail image
    15.             if (id == null || server == null || secret == null) {
    16.                 return;
    17.             }
    18.             String strImageUrl = String.format(IMAGE_URL_FORMAT,
    19.                     server, id, secret);
    20.             retrieveImage(strImageUrl);
    21.         }
    22.     }//GEN-LAST:event_listImagesValueChanged
    23.    
    24.     private void retrieveImage(String imageUrl) {
    25.         // SwingWorker objects can"t be reused, so
    26.         // create a new one as needed.
    27.         ImageRetriever imgRetriever = new ImageRetriever(lblImage, imageUrl);
    28.         progressSelectedImage.setValue(0);
    29.         // listen for changes in the "progress" property
    30.         // we can reuse the listener even though the worker thread
    31.         // will be a new SwingWorker
    32.         imgRetriever.addPropertyChangeListener(listenerSelectedImage);
    33.         
    34.         progressSelectedImage.setIndeterminate(true);
    35.         
    36.         // tell the worker thread to begin with this asynchronous method
    37.         imgRetriever.execute();
    38.         // this event thread continues immediately here without blocking
    39.         
    40.     }
    41.    
    复制代码
      注意每次下载新的图像,retrieveImage方法都生成新的ImageRetriever实例。SwingWorker实例不可复用,每次执行任务必须生成新的实例。  正确使用SwingWorker的方法是实例化,如果需要获知线程发生的状态变化通知,则要添加属性变化处理器,最后执行。execute方法是异步执行,它立即返回到调用者。在execute方法执行后,EDT立即继续执行。  前面所讲的retrieveImage方法生成一个ImageRetriever,并向该实例提供一个JLabel引用,任务线程使用它显示图象,retrieveImage方法和EDT不需要再访问任务线程。这些代码生成线程、执行、并根据需要继续处理UI事件。  注意retrieveImage方法在执行线程之前往ImageRetriever实例添加属性改变处理器,MainFrame类包含进度条跟踪当前图像下载的状态,事件处理器会接收到所有SwingWorker线程事件的通知,其中一个事件是进度事件。  下面的事件处理器类响应进度事件,更新进度条。本文程序有两个进度条,一个跟踪搜索以及下载缩略图进度,另一个跟踪下载大图片的进度。程序使用相同的 ProgressListener类,不同实例,来跟踪任务进度,因此需要在ProgressListener构造函数中传要更新的进度条,下面是 ProgressListener的代码:
    1. class ProgressListener implements PropertyChangeListener {
    2.         // prevent creation without providing a progress bar
    3.         private ProgressListener() {}
    4.         
    5.         ProgressListener(JProgressBar progressBar) {
    6.             this.progressBar = progressBar;
    7.             this.progressBar.setValue(0);
    8.         }
    9.         
    10.         public void propertyChange(PropertyChangeEvent evt) {
    11.             String strPropertyName = evt.getPropertyName();
    12.             if ("progress".equals(strPropertyName)) {
    13.                 progressBar.setIndeterminate(false);
    14.                 int progress = (Integer)evt.getNewValue();
    15.                 progressBar.setValue(progress);
    16.             }
    17.         }
    18.         
    19.         private JProgressBar progressBar;
    20.     }
    复制代码
       ImageRetriever类的使用是简单的,给定一个图像URL,它负责下载该图像,向事件处理器提供进度信息来更新MainFrame类的进度条,你不需要知道关于创建和使用任务线程任何信息。只要一点努力,就可以从SwingWorker子类获取更多效果。下一节将显示如何实现并使用更复杂的任务子类。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-25 07:31 , Processed in 0.370237 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表