`

Eclipse中的图片资源管理

    博客分类:
  • SWT
阅读更多

在本文中,将讨论如下内容:

  1、 系统资源,为后面讨论图片资源做一铺垫

  2、 SWT中的图片资源管理

  3、 Display hook销毁机制,JFace中图片资源管理的重要基础

  4、 JFace中的ImageDescriptor

  5、 JFace中的图片资源管理(ImageRegistry)

  6、 JFace中图片资源管理ImageRegistry所适用的场景和使用规则

  7、 Eclipse中插件share images机制

  8、 在Eclipse插件开发或者开发RCP程序时,使用图片资源需要的注意事项

  【系统资源】

  众所周知,Java开发人员在使用SWT/JFACE的时候,并不能借助于Java内置的垃圾回收机制来彻底完成系统资源的清理(Java虚拟机只能帮助我们释放虚拟机内存中的系统资源句柄引用对象)。在SWT中系统资源对象的定级类型是org.eclipse.swt.graphics.Resource,在类型明确说明了“Resources created by the application must be disposed”,这也让我们想起了关于Image使用的一句名言“谁创建,谁负责”,当然,这个原则也同样适用于其他类型的系统资源。

  我们之所以如此关注系统资源的使用,尤其是臭名昭著的图片资源,主要是因为我们怕了系统资源泄漏引起的系统crash的问题。例如org.eclipse.swt.SWTError: No more handles异常有可能在我们试图创建图片资源的时候发生,这说明当前系统句柄已经不足,造成这个问题的罪魁祸首当然是我们写代码的人。

  【SWT中的图片资源管理】

  我们直接看一下SWT中图片资源类型的定义(org.eclipse.swt.graphics.Image),在类型说明中明确指出了:“Application code must explicitly invoke the Image.dispose() method to release the operating system resources managed by each instance when those instances are no longer required”。我们再看一下另外一个我们熟悉的类型org.eclipse.swt.graphics.ImageData,我们可以将其看作是Image对应的元数据模型对象,描述了具体创建Image需要的信息。

通过上面的说明,我们发现SWT唯一告诉我们的是:自己创建的图片资源,自己负责去销毁,通过调用Image.dispose()。那我们在使用SWT的时候,应该如何释放图片资源呢?

  我们知道SWT的widget在销毁的时候,也会销毁子widget,所以,覆写你自己的Component对应的dispose方法,将你使用的系统资源销毁。目前,也只能这样了~_~。如果觉得不满意,接着看下面的Display hook销毁机制。

  【Display hook销毁机制】

  在Display device中,我们看了如下一个hook接口:

  /**
   *Causesthe<code>run()</code>methodoftherunnableto
   *beinvokedbytheuser-interfacethreadjustbeforethe
   *receiverisdisposed.
   */
    public void disposeExec (Runnable runnable) {
       //注册用户自定义runnable,在display release的时候回调此runnable
       将runnable注册到disposeList
    }
    disposeList中的线程会在display release的时候被调用,如下:
    /**
*Releasesanyinternalresourcesbacktotheoperating
*systemandclearsallfieldsexceptthedevicehandle.
*/
    protected void release () {
       ……
       //会执行用户注册的销毁线程
       if (disposeList != null) {
           for (int i=0; i<disposeList.length; i++) {
              if (disposeList [i] != null) disposeList [i].run ();
           }
       }
       ……
    }

 看来,SWT并没有把事情做绝了,还是给开发者留下一条后路的。Display允许开发者注册一个自定义线程hook到Display的release过程,开发者可以用如下方式来确保开发者使用的系统资源在Display release的时候被销毁:

display.disposeExec(new Runnable() {
       public void run() {
           //销毁系统资源的逻辑代码
           image.dispose();
           …….
       }  
    });

  以上方式其实也是JFace中图片资源管理(ImageRegistry、ResourceManager)能够确保Display release的时候能够彻底释放被ImageRegistry托管的图片资源。

  到这里回顾一下,SWT中资源释放的途径吧:

  1、 覆写相应Component对应的dispose方法。这有别于Display的hook机制,因为其能够在Display运行期间(未被release之前)就释放掉系统资源,最好的方式。

  2、 利用Display的hook机制,确保在Display被release的时候能够销毁资源。注意,请不要过多依赖此方式,因为很容易造成在Display被release之前,已经发生了系统crash的问题。

  【JFace中图片资源管理--ImageDescriptor】

  前面我们已经见过SWT中的Image和ImageData类型了,在继续下面的内容之前,我们先看一下在JFace中我们最常用来创建图片资源的一个工厂类:ImageDescriptor。在ImageDescriptor的类型说明中告诉我们,有两种使用ImageDescriptor创建图片的方式,分别通过createImage和createResource接口,“There are two ways to get an Image from an ImageDescriptor. The method createImage will always return a new Image which must be disposed by the caller. Alternatively, createResource() returns a shared Image. When the caller is done with an image obtained from createResource, they must call destroyResource() rather than disposing the Image directly.”。分析如下:

首先看一下createResource方式,ImageDescriptor是一种DeviceResourceDescriptor,后者的对外操作如下:

/**
    *Createstheresourcedescribedbythisdescriptor
     */
    public abstract Object createResource(Device device) throws DeviceResourceException;
    /**
   *Undoeseverythingthatwasdonebyapreviouscalltocreate(...)
     */
    public abstract void destroyResource(Object previouslyCreatedObject);

 

这也就是说,ImageDescriptor提供了createResource / destroyResource接口来负责创建和销毁Image资源。请注意这边的一点,在孤立使用ImageDescriptor(没有配合ResourceRegistry使用,例如ImageRegistry)的时候,用户还是要负责通过调用destroyResource来释放创建的资源。

  其次来看一下createImage的方式:

    /**
*The returnedimagemustbeexplicitlydisposedusingtheimage'sdispose call.Theimagewillnotbeautomaticallygarbagecollected. */
       public Image createImage(boolean returnMissingImageOnError, Device device) {}

 

这也就是说,ImageDescriptor提供的createImage的方式,也需要用户来显示的销毁资源。那createImage和createResource两种方式之间的差别是什么呢?稍微分析一下ImageDescriptor的这两种创建方式的实现,我们就可以看出来差别:

  1、 createImage每次都创建一个全新的图片资源(图片资源的创建是很耗时的~_~)

  2、 createResource的方式采用了缓存的方式复用已经创建过的资源,并不是每次都创建一个全新的资源。这一点虽然带来了性能的提高,但是并没有解决图片资源释放的问题,倒是给开发者留下了一种假象,造成了随便使用ImageDescriptor的问题,反而造成了大量的图片资源(当然,更多的是由于调用createImage的方式造成的,因为每次都创建一个全新的图片资源)没有释放。

到现在为止,我们看到JFace已经对SWT中的图片资源的管理做了一个小的补充:提供了ImageDescriptor.createResource的方式,可以利用缓存效果,能够减少不必要的图片系统资源创建,而且效率有所提高。关于如何释放,可以参考SWT中覆写Component.dispose的方式,例如在label provider使用的图片资源,可以覆写对于provider的dispose方法,JFace框架会自动调用。

  【JFace中图片资源管理--ImageRegistry & ResourceManager】

  下面,我们接着看一下JFace中的ImageRegistry的实现原理。

  首先我们看一下JFace中的资源管理门面类(façade class)JFaceResources,我们由它来获取我们的JFace ImageRegistry:

public static ImageRegistry getImageRegistry() {
   if (imageRegistry == null) {
imageRegistry = new ImageRegistry(getResources(Display.getCurrent()));
   }
   return imageRegistry;
}
public static ResourceManager getResources(final Display toQuery) {
  ResourceManager reg = (ResourceManager)registries.get(toQuery);
  if (reg == null) {
    final DeviceResourceManager mgr = new DeviceResourceManager(toQuery);
        //向Display hook了销毁线程
toQuery.disposeExec(new Runnable() {
     public void run() {
       mgr.dispose();
       registries.remove(toQuery);
     }
});
  }
  return reg;
}

 

分析了一下ResourceManager(DeviceResourceManager)的实现,我们发现:DeviceResourceManager就是对DeviceResourceDescriptor(ImageDescriptor)进行了引用计数管理。通过JFaceResources.getResources利用了前面说的Display的hook销毁机制(注意,如果不通过JFaceResources.getResources来获取ResourceManager,则不会默认享受Display的hook销毁机制,需要自己向Display注册),确保由被托管ImageDescriptor创建的残留在系统中的图片资源在Display release的时候会被彻底销毁。核心方法如下:

create(DeviceResourceDescriptor descriptor) 

  //如果是首次注册,创建引用技数,allocate资源并对资源进行缓存

  //如果是已经注册,增加引用技数,直接返回缓存的系统资源

  destroy(DeviceResourceDescriptor descriptor) //将

  //如果引用技术==1,通过调用deallocate彻底销毁资源

  //如果引用技术>1,削减引用计数(系统资源不会被销毁)

  那就是说,如果一个ImageDescriptor被ResourceManager托管了,那由它创建的资源(注意:通过ImageDescriptor.createResource的方式)由两种销毁的途径:

  1、      如果不通过JFaceResources.getResources的方式,单独使用ResourceManager,则只能利用ResourceManager的引用计数管理来销毁资源(引用计数为0时),通过显示调用ResourceManager.destroy来削减引用计数。

  2、      如果通过JFaceResources.getResources来使用ResourceManager,则除了能够使用到引用计数管理资源,同时也默认使用了Display的hook销毁机制,JFace的ImageRegistry也很好的利用了这一点。

  现在回头看一下ImageRegistry提供的核心操作,着重分析一下ImageRegistry在利用了ResourceManager对ImageDescriptor进行管理的基础上,做了那些补充:

put(String key, Image image)       //注册image
put(String key, ImageDescriptor descriptor) //注册descriptor
Image get(String key)          //获取imge
ImageDescriptor getDescriptor(String key)  //获取descriptor
remove(String key)           //取消注册
dipose()                //销毁资源

 

通过对ImageRegistry简要的分析之后,我们的结论如下:

  1、 如果以put(String key, ImageDescriptor descriptor)的方式注册,ImageRegistry直接讲descriptor委托给ResourceManager委托管理,自己并不承担管理任务。而且,ImageRegistry对这种方式注册的ImageDescriptor所创建的系统图片资源的销毁也委托给ResourceManager进行,并不是在以上自己的dispose方法中进行,而是在ResourceManager.dispose方法中进行。

  2、 如果以put(String key, Image image)的方式注册,ImageRegistry做了部分的补充管理,其将image包装进自己的OriginalImageDescriptor(ImageRegistry的一个内部类,继承自ImageDescriptor,对图片资源本身增加引用计数)实现中,并对image本身进行了引用计数管理。同时,对这种方式注册的图片资源的销毁是ImageRegistry自己承担的,在自身的dispose方法中完成。(注意,在ImageRegistry的构造方法中,将ImageRegistry.dispose封装为一个runnable注册到了ResourceManage的dispose过程中,而ResourceManage.dispose已经在JFaceResources.getResources方法中被hook到了Display的资源销毁过程中)。

  3、 通过1和2的结论,JFace ImageRegistry对系统资源的销毁已经做了两手准备,

  其并不希望用户自己来销毁资源(无论是通过Image.dispose还是ImageDescriptor.destoryResource,或者ImageRegistry.dispose),当然,ImageRegistry允许通过remove接口来取消注册。

  JFaceResources

  +提供hook机制

  ImageRegistry

  +自己管理部分资源

  ResourceManager

  +管理ImageDescriptor及其创建的资源

  【ImageRegistry的适用场景和使用规则】

通过上面的实现原理分析,我们知道ImageRegistry并不欢迎用户来过多地参与图片资源的释放过程,所以ImageRegistry适用于如下场景:

  1、 决定共享和高度复用的图片资源。这种资源一般是被使用的特别频繁,同时,不急于销毁,只要在Display release的时候销毁掉就可以了,所以既可以利用到图片资源本身缓存的优势(减少物理创建的次数),又可以利用其Display的hook销毁机制,确保会被销毁。

  2、 用户可以直接使用ImageRegistry(不通过JFaceResources.getImageRegistry的方式使用),复用部分ImageRegistry的管理功能,开发自己的缓存策略,但是,要确保自己会在合适的地方调用ImageRegistry.dispose方法来销毁registry。Eclipse Workbench中的shared images机制就用了这一点。

  ImageRegistry的使用规则如下:

  1、 谁创建,谁负责。具体图片资源的创建是由ImageRegistry负责的,用户既然托管了,就不应该再干预资源的释放。而且,注册进ImageRegistry的资源是共享的,一个用户释放了,会影响到其他用户的使用。当然,对于比较熟悉JFace ImageRegistry原理的开发者,可以参与到引用计数的管理,通过这种方式,以安全的、不影响其他用户使用的方式来间接参与释放的过程。

  2、 非共享图片资源请不要交由ImageRegistry托管。对于一个仅限于局部使用而且使用并不是十分频繁的图片资源,这样做不会带来什么好处,而且,尤其是对于不能参与到引用计数管理的初级用户,这样做反而会使得一个本可以马上释放的图片资源反而会一直占用,直到Display release的时候才销毁。

  3、 要投入精力对ImageRegistry的key值进行管理,否则,会引起混乱。因为ImageRegistry本质上可以看作Eclipse平台中的一个全局对象,对其含有的key列表的管理是再所难免。

【Eclipse中插件share images机制】

  在Eclipse,一个插件可以暴露(expose)自己的图片资源,以便提供给需要的插件使用,我们就称它为插件之间的share images机制吧。上面提到过了,这其实是部分复用了JFace ImageRegistry的管理机制。

  如何共享(可以参照Workbench插件的share images实现):

  1、 按照默认约定,创建一个ISharedImages接口,提供有意义key值

  2、 实现自己创建的ISharedImages接口,并结合ImageRegistry来管理图片资源;并提供显示的dipose公共接口,负责释放自己管理的图片资源

  3、 在自己的插件中暴露ISharedImages

  4、 在合适时机,调用ISharedImages.dispose来释放资源。这个时机一般选择在Plugin stop的时候比较合适,当然,也可以选择在其他时机。

  如何使用:

  1、 获取目标插件的ISharedImages实现,并通过ISharedImages提供的key值来获取特定的图片资源。以workbench插件share images为例:

  PlatformUI.getWorkbench().getSharedImages().getImage(key)

  2、暴露图片资源的插件负责图片资源的创建和销毁,其他插件不要参与销毁过程。换句话说,还是要遵守谁创建、谁负责的原则。以workbench插件share images为例:

  在workbench close的时候,会间接调用ISharedImages.dispose()。

  【Eclipse中使用图片资源的经验总结】

  1、 坚持“谁创建,谁负责”的原则。分为如下:

  a)     如果是用户自己创建的,请自己释放。例如通过覆写Component对于的dispose方法、通过覆写label provider对应的dispose方法等等,这对于一些适用于局部的图片资源较为适合;当然,也可以变态利用Display的hook释放机制(但是,一般对于长期使用的资源才会这样做!!!)。

b)    如果是通过JFaceResources.getImageRegistry的方式使用ImageRegistry时,请不要释放资源,让ImageRegistry自己解决。一般使用于比较频繁使用的全局共享图片资源,例如想保持风格统一的图片资源等。

  c)    如果是使用了IShareImages的机制,请提供图片资源的插件自己负责释放。如何使用这种机制,最好参照eclipse中已有的实现,保持风格统一,“有样学样”吧。

  2、 正确认识系统资源泄漏引起的crash问题的原因。导致原因有两种:

  a)     首先,是没有释放,导致泄漏。请参照上面的“谁创建,谁负责”的原则。

  b)    其次,是释放的过晚,导致积累过多。例如本来应该立即释放的资源,反而通过ImageRegistry进行了托管,同时有没有控制引用计数的管理,导致到了Display release的时候才释放资源。同样道理,本来不需要暴露给其他插件贡献的图片资源,反而暴露了,导致释放过完等。

  3、 正确认识系统资源的创建和销毁所带来的时间消耗,这是从系统性能的角度考虑。例如,可以用ImageDescriptor.createResource的方式替换原始的new Image的方式,减少创建资源过于频繁和销毁资源过于频繁所带来的时间占用。对于需要长期使用的贡献资源,可以使用ImageRegistry的方式等等。

  4、 对于特殊的场景,可以在参考以上原理(例如JFace中的图片管理的实现原理分析)的基础上自己实现图片资源的管理策略。这对团队开发产品的情况下尤其适用,一方面可以优化管理策略,使之更切近团队应用;再者,可以减少JFace ImageRegsitry使用的复杂度,并减少误用。例如,我们可以把插件间share images的机制看成是对JFace ImageRegsitry的灵活使用。

 

  5、 无论使用那种管理策略(无论是来自eclipse还是其他),使用这前一定要仔细看API说明,并简要分析一下实现原理。对于做上规模的插件产品/应用来讲,毕竟对图片这种系统资源的管理太重要了!!!对于做较为简单的开发,基本上本着“谁创建、谁负责”的原则,用完之后在自己感觉合适的地方销毁掉就可以了,完全可以不去碰JFace中的ImageRegistry那套东东,引来不必要的负责度和复用,尤其是对于新手来说。

<!----><!----><!----><!----><!----><!---->
分享到:
评论

相关推荐

    eclipse资源文件插件

    1. **图形化资源管理**:提供一个直观的界面,用于浏览、添加、删除和修改项目中的非源码文件,例如图片、配置文件等。 2. **版本控制集成**:与Git、SVN等版本控制系统集成,方便资源文件的版本管理和协同编辑。 3....

    eclipse 快捷键图片可做桌面(绝对原创)

    "eclipse 快捷键图片可做桌面"的资源旨在为用户提供一个便捷的方式来记忆和查阅这些常用快捷键。 "eclipse-shorcut-wallpaper.png"很可能是一张包含Eclipse主要快捷键的图像壁纸,设计得适合作为桌面背景。这样的...

    ECLIPSE写的一个学生信息管理系统网页版

    - **资源文件**:如图片、CSS样式表和JavaScript文件,用于增强用户体验。 在实际开发过程中,可能还需要考虑安全性(如SQL注入防护)、性能优化(如使用缓存)、错误处理和日志记录等方面。同时,遵循MVC(Model-...

    用eclipse 写的学生管理系统

    通常,Java项目包含src/main/java目录存放源代码,src/main/resources存放非编译资源,如配置文件、图片等。在“src/main/java”下创建对应的包结构,例如“com.example.stumanager”,然后在此包下创建主类“Main....

    eclipse快速跳到资源库管理器插件explorer_4.1.0.zip

    "explorer"插件是Eclipse中的一个资源管理器视图,它提供了一种更直观的方式来浏览和管理项目中的文件和目录。此插件版本为4.1.0,通常与Eclipse的版本兼容性有关,确保在特定Eclipse版本下正常运行。 2. **快捷键...

    eclipse svn中文插件

    - `content.jar`:包含插件的资源和内容,如图片、样式表等。 - `artifacts.jar`:可能包含了插件的元数据和构建信息。 - `site.xml`:Eclipse插件更新站点的描述文件,用于指明插件的版本、依赖和其他元数据。 - `...

    eclipse资源文件编辑器插件

    在实际开发中,比如Android应用开发,资源文件编辑器插件对于处理XML布局文件、图片资源、字符串资源等非常有用。在游戏开发中,它可能涉及到音频、视频、模型等多媒体资源的管理。 安装此类插件的方法通常是通过...

    安卓清理eclipse无用资源

    在Android开发过程中,保持项目整洁和优化资源管理是至关重要的。"安卓清理eclipse无用资源"的主题旨在帮助开发者高效地清理adt(Android Development Toolkit)目录下不再使用的代码和资源,从而减小工程的体积,...

    Eclipse格式模板XML文件及图片使用

    Eclipse是一款广泛使用的开源集成开发环境(IDE),尤其在Java开发者中非常流行。为了提高代码质量和团队协作效率,Eclipse提供了自定义...同时,合理使用Eclipse的图片资源管理功能,也能让开发环境和文档更加专业。

    Eclipse中 open in explorer 插件 定位资源 项目中的资源文件

    这个插件允许用户在Eclipse内直接右键点击项目或文件,选择“在文件系统中打开”选项,从而快速地在Windows的资源管理器(或其他操作系统对应的文件管理器)中打开对应的位置。这对于查看、编辑或调试项目文件,尤其...

    jsp+Eclipse图书管理系统

    开发者可以创建Web项目,将JSP文件、Servlet、CSS、JavaScript、图片和其他资源组织在一个结构化的项目中。Eclipse还支持Tomcat、Jetty等Web服务器的集成,使得开发和测试过程更加顺畅。 在【压缩包子文件的文件...

    支持Eclipse的fresco图片加载框架

    - **Cache**: 包括Memory Cache(内存缓存)和Disk Cache(磁盘缓存),有效管理图片资源。 3. **对比ImageLoader**: - ImageLoader是一个广泛使用的Android图片加载库,但它在处理大量图片或复杂场景时可能...

    资源文件编辑器(eclipse插件)

    资源文件编辑器是Eclipse集成开发环境中的一个重要组成部分,它为开发者提供了便捷的方式来管理和修改项目中的各种非代码资源。在Eclipse中,这些资源可能包括文本文件、图像、配置文件、XML布局等,广泛应用于Java...

    eclipse 图片预览插件

    【标题】:Eclipse图片预览插件安装与使用指南 ...通过安装并使用这款Eclipse图片预览插件,开发者可以更加高效地处理图像资源,提高开发流程的流畅度,使得Eclipse成为了一个更为全能的开发工具。

    CMS.rar_cms Eclipse_cms客户单机_eclipse mysql_mysql eclipse_客户信息管理系统

    【标题】"CMS.rar" 是一个压缩包文件,其中包含了构建和运行一个客户信息管理系统的全部...通过这个项目,开发者可以深入理解如何在实际环境中使用Eclipse进行开发,以及如何设计和实施一个简单的客户信息管理系统。

    在eclipse4中创建fragment

    - **资源和本地化**:Fragment可以拥有自己的资源文件(如图片、文本文件)和本地化字符串。这些可以通过“Resources”和“Localization”选项卡进行管理。 - **扩展点和贡献**:Fragment可以利用Eclipse的扩展点...

    eclipse jsp 旅游网站源码 +数据库

    2. **Web内容文件夹(WebContent)**:存放HTML、JSP、图片、JavaScript、CSS等Web资源。 3. **配置文件**:如web.xml,定义了Web应用的部署描述符,指示服务器如何处理请求。 4. **数据库脚本**:如SQL文件,用于...

    Fresco 强大的图片资源加载框架 支持gif eclipse版

    Fresco是一款由Facebook开发并开源的强大图片资源加载框架,它专为Android平台设计,旨在解决在处理大量图片时出现的性能问题,如内存管理、图片重用和用户体验优化。Fresco不仅支持静态图片的加载,还特别强调了对...

    慕林财务管理软件,,java界面设计,Eclipse基本应用

    2. 资源文件:可能包含GUI设计所需的图片、字体、配置文件等。 3. 构建脚本:如Ant或Maven脚本,用于自动化构建和部署过程。 4. 数据库脚本:可能包含SQL文件,用于初始化和管理数据库结构。 5. 文档:可能有设计...

    eclipse插件_OpenExplorer

    OpenExplorer插件的诞生,旨在弥补Eclipse原生资源管理器在文件操作上的不足。在默认情况下,Eclipse的资源浏览器虽然能够满足基本需求,但在处理大型项目或者频繁进行文件操作时,可能会显得力不从心。OpenExplorer...

Global site tag (gtag.js) - Google Analytics