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入门到精通教程
查看: 411|回复: 0

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

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

    [LV.1]初来乍到

    发表于 2014-11-17 23:58:00 | 显示全部楼层 |阅读模式
    实现ImageSearcher     SwingWorker的子类可能既会生成最终结果也会产生中间结果,记住线程在doInBackground方法结束后才产生最后结果,但任务线程也可以产生和公布中间数据。比如当ImageSearcher类从Flickr Web服务中获取缩略图列表时,每当下载一个缩略图时,列表便应显示这个缩略图,没理由要等待所有匹配图像下载完毕才把结果放在列表中。     实现SwingWorker子类时,在类声明处要指定最终和中间结果的类型,ImageSearcher搜索并下载匹配的缩略图。

        由于该类在任务结束时产生匹配图像的列表,所以该类使用List作为类的类型参数,为表明它中间发布的数据是匹配图片,它还使用ImageInfo作为类型参数。

    ImageSearcher的定义如下:
      
      
       
       
         
       

         
       
      
    1. public class ImageSearcher extends SwingWorker< List< ImageInfo>, ImageInfo> {
    2.   public ImageSearcher(DefaultListModel model, String key, String search, int page) {
    3.     this.model = model;
    4.     this.key = key;
    5.     this.search = search;
    6.     this.page = page;
    7.   }
    8.   ...
    9. }
    复制代码
    这部分说明了几点:
        首先List<ImageInfo>类型参数说明任务结束时ImageSearcher的doInBackground和 get方法返回一个ImageInfo对象列表;
        其次当类下载图像时它会发布一些ImageInfo对象,在它们可用后可以立即可以显示出来。

    因为类的构造函数参数之一是列表模型,因此任务线程会直接更新模型。正如后面看到的一样,它的确是直接更新列表模型;另外,任务线程需要一个Flickr API主键(由Flickr提供)和一个查询项。因为该web服务使用分页方式提供结果,还需要一个页码参数来决定选择哪些匹配的图集。为方便起见,该演示总是返回匹配页面的第一页。因为doInBackground方法是任何任务线程的重心,先来看以下ImageSearcher的实现:

    1. @Override
    2. protected List< ImageInfo> doInBackground() {
    3.   ...
    4.   Object strResults = null;
    5.   InputStream is = null;
    6.   URL url = null;
    7.   List< ImageInfo> infoList = null;
    8.   try {
    9.     url = new URL(searchURL);
    10.     is = url.openStream();
    11.     infoList = parseImageInfo(is);
    12.     retrieveAndProcessThumbnails(infoList);
    13.   } catch(MalformedURLException mfe) {
    14.     ...
    15.   }
    16.   return infoList;
    17. }
    复制代码
    它从Web服务打开一个流,提供一个查询URL,parseImageInfo方法产生匹配图片的信息列表,解析由该web服务返回的一个XML文件。retrieveAndProcessThumbnails方法使用解析过的列表下载所有的缩略图。最终结果是一个完整的包含缩略图数据的ImageInfo对象列表。infoList对象同先前提到的类和方法定义类型相同,是List<ImageInfo>类型。  该类同ImageRetriever类相似,因为它也需要更新进度条,并提供图像数据。本文不再详细叙述ImageSearcher的 doInBackground、done、get和setProgress方法,因为它们基本上同前面类中的方法相似。但是,ImageSearcher 类不仅仅下载单个图片,它还要下载匹配的前100个缩略图片,这儿是演示SwingWorker其他功能的好地方:publish和process方法。  你可以使用publish方法来发布要处理的中间数据,当ImageSearcher线程下载缩略图时,它会随着下载而更新图片信息列表,还会发布每一批图像信息,以便UI能在图片数据到达时显示这些图片。如果SwingWorker子类发布了一些数据,那么也应该实现process方法来处理这些中间结果。任务对象的父类会在EDT线程上激活process方法,因此在此方法中程序可以安全的更新UI组件。  下面代码显示了ImageSearcher是如何使用publish和process方法的:
    1. private void retrieveAndProcessThumbnails(List< ImageInfo> infoList) {
    2.   for (int x=0; x < infoList.size() && !isCancelled(); ++x) {           
    3.     // http://static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg
    4.     ImageInfo info = infoList.get(x);
    5.     String strImageUrl = String.format("%s/%s/%s_%s_s.jpg",
    6.          IMAGE_URL, info.getServer(), info.getId(), info.getSecret());
    7.     Icon thumbNail = retrieveThumbNail(strImageUrl);
    8.     info.setThumbnail(thumbNail);
    9.     publish(info);
    10.     setProgress(100 * (x+1)/infoList.size());
    11.   }
    12. }   
    13. /**
    14. * Process is called as a result of this worker thread"s calling the
    15. * publish method. This method runs on the event dispatch thread.
    16. *
    17. * As image thumbnails are retrieved, the worker adds them to the
    18. * list model.
    19. *
    20. */
    21. @Override
    22. protected void process(List
    23.    infoList) {
    24.   for(ImageInfo info: infoList) {
    25.     if (isCancelled()) {
    26.       break;
    27.     }
    28.     model.addElement(info);
    29.   }     
    30. }
    复制代码
       为在任务执行中而非任务结束时发布数据,要调用publish方法,并以参数的形式提供要发布的数据。当然像前面所说的那样,必须在类声明中指定中间数据的类型。在本例中这个类型是ImageInfo。前面所述retrieveAndProcessThumbnails方法显示了如何在线程下载缩略图时发布ImageInfo对象。     当从任务线程调用publish方法时,SwingWorker类调度process方法。有意思的是process方法是在EDT上面执行,这意味着可以同Swing组件和其模型直接交互。process方法将ImageInfo对象的缩略图添加到列表模型中,这样图片就会立即显现在列表中。    注意process方法的参数,它并没有使用单个ImagInfo对象,而是这种对象的一个列表。原因是publish方法能够以批模式来调用process方法,就是说,每个publish调用并不总是产生相应的process调用。如果可能,publish方法会收集对象并以对象的列表为参数调用process方法。实现process方法要以对象列表的方式处理,就像下面的代码: @Override protected void process(List< ImageInfo> infoList) {
       for(ImageInfo info: infoList) {
          ...
         model.addElement(info);
        }
    }  如果想允许程序用户取消任务,实现代码要在SwingWorker子类中周期性地检查取消请求。调用isCancelled方法来检查是否有取消请求。 ImageSearcher代码的许多地方都有isCancelled方法的调用,在循环迭代或者其他检查点调用这个方法确保线程能即时获得取消请求。线程周期性地检查这种请求并停止工作。比如ImageSearcher类在以下几个点检查取消请求:  * doInBackground方法的子任务在获取每个缩略图之前
    * process方法中在更新GUI列表模型之前
    * done方法中在更新GUI列表模型最终结果之前  doInBackground方法调用retrieveAndProcessThumbnails方法,该方法循环列表的图像数据并获取这些图像的缩略图。然而当任务线程正在执行循环时,用户可以启动新的查询。因此这儿也需要检查取消请求: private void retrieveAndProcessThumbnails(List<ImageInfo> infoList) {  for (int x=0; x<infoList.size(); ++x) {
        // Check whether this thread has been cancelled.
        // Stop all thumbnail retrieval.
        if (isCancelled()) {
           break;
        }
    ...
    }  该类处理缩略图的同时就发布它们,其结果是在EDT上运行process方法。如果用户请求取消,或者启动新的搜索,可以通过在process方法内检查来避免这种情况的发生。 protected void process(List<ImageInfo> infoList) {      for (ImageInfo info: infoList) {
          if (isCancelled()) {
              break;
          }
         model.addElement(info);
        }
    }  最后,一旦任务线程完成,它还有一个机会更新GUI的模型,这就是在done方法中。因此在这儿也要检查取消请求: @Override
    protected void done() {
       ...
       if (isCancelled()) {
        return;
       }
       ...
       // Update the model.
    }  ImageSearcher类是一个更为完整的SwingWorker的例子,它比ImageRetriever做的更多,ImageSearcher类往GUI上发布中间数据,并处理任务取消请求。两个类都在后台执行任务,并通过事件处理器跟踪进度。 使用ImageSearcher类    演示程序提供一个搜索输入栏。当用户输入图片查询条件时,MainFrame类创建一个ImageSearcher实例,输入一个查询条件并产生一个键盘事件,输入栏的键盘事件激活searchImage方法,该方法实例化一个ImageSearcher对象并执行之:
    1. private void searchImages(String strSearchText, int page) {
    2.   if (searcher != null && !searcher.isDone()) {
    3.     // Cancel current search to begin a new one.
    4.     // You want only one image search at a time.
    5.     searcher.cancel(true);
    6.     searcher = null;
    7.   }
    8.   ...
    9.   // Provide the list model so that the ImageSearcher can publish
    10.   // images to the list immediately as they are available.
    11.   searcher = new ImageSearcher(listModel, API_KEY, strEncodedText, page);
    12.   searcher.addPropertyChangeListener(listenerMatchedImages);
    13.   progressMatchedImages.setIndeterminate(true);
    14.   // Start the search!
    15.   searcher.execute();
    16.   // This event thread continues immediately here without blocking.
    17. }
    复制代码
      注意代码在ImageSearcher构造函数中提供一个listModel作为参数,这个模型允许任务线程能直接访问更新列表内容。也可以向任务对象添加一个属性改变处理器。上文添加了一个属性改变处理器来更新进度条。除此外还需要添加一个处理器以响应任务线程的状态变化,特别是要侦听DONE状态并使用前文提到的get方法获取任务结果。    一旦执行任务线程,就会搜索并下载缩略图。该程序向任务对象提供了一个列表模型,因此它会直接更新列表。另外,ImageSearcher类提供了中间数据,因此可以在下载图片的同时更新JList组件。其运行的直接效果就是改善了程序的性能。如下图8所示,搜索结果是列表中显示的小图片:
      缩略图是任务线程发布的中间数据    你可以通过调用其cancel方法取消SwingWorker线程。用户可以在当前搜索正在进行时输入新的搜索条件并提交来取消当前图像搜索。搜索输入栏的事件处理器检查现有线程是否正在运行,如果正在运行则调用cancel来取消之: private void searchImages(String strSearchText, int page) {
        if (searcher != null && !searcher.isDone()) {
           // Cancel current search to begin a new one.
          // You want only one image search at a time.
          searcher.cancel(true);
          searcher = null;
        }
        ...
       }  当调用cancel方法时,代码产生新的任务实例,每一个新的搜索需要自己的任务实例。 总结
        所有的GUI事件和交互都运行在EDT上,在EDT上运行耗时或者I/O密集型处理会导致界面变得缓慢失去响应,为改善这种状况应使用java SE 6中提供的SwingWorker类将这些任务转移到任务线程中。  使用SwingWorker,你可以执行相同的任务而不会延迟EDT运行,会提高程序的性能。并且,任务线程可以安全同界面组件交互,因为有回调方法可以在EDT上运行,允许任务运行和完成时更新GUI组件。
      


      
      
       
       

         
       

         
       
      
    复制代码
    回复

    使用道具 举报

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

    本版积分规则

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

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

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

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