<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Live a simple life

    沉默(zhu_xing@live.cn)
    隨筆 - 48, 文章 - 0, 評論 - 132, 引用 - 0
    數據加載中……

    【原創】Eclipse插件開發:Eclipse中的圖片資源管理

     

    Eclipse中的圖片資源管理

                                                             朱興(zhu_xing@live.cn

    【概述】

    在本文中,將討論如下內容:

    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的時候,應該如何釋放圖片資源呢?

           我們知道SWTwidget在銷毀的時候,也會銷毀子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允許開發者注冊一個自定義線程hookDisplayrelease過程,開發者可以用如下方式來確保開發者使用的系統資源在Display release的時候被銷毀:

    display.disposeExec(new Runnable() {

                  public void run() {

                         //銷毀系統資源的邏輯代碼

                         image.dispose();

                         …….

                  }    

           });

    以上方式其實也是JFace中圖片資源管理(ImageRegistryResourceManager)能夠確保Display release的時候能夠徹底釋放被ImageRegistry托管的圖片資源。

           到這里回顧一下,SWT中資源釋放的途徑吧:

    1、 覆寫相應Component對應的dispose方法。這有別于Displayhook機制,因為其能夠在Display運行期間(未被release之前)就釋放掉系統資源,最好的方式。

    2、 利用Displayhook機制,確保在Displayrelease的時候能夠銷毀資源。注意,請不要過多依賴此方式,因為很容易造成在Displayrelease之前,已經發生了系統crash的問題。

    JFace中圖片資源管理--ImageDescriptor

           前面我們已經見過SWT中的ImageImageData類型了,在繼續下面的內容之前,我們先看一下在JFace中我們最常用來創建圖片資源的一個工廠類:ImageDescriptor。在ImageDescriptor的類型說明中告訴我們,有兩種使用ImageDescriptor創建圖片的方式,分別通過createImagecreateResource接口,“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的方式,也需要用戶來顯示的銷毀資源。那createImagecreateResource兩種方式之間的差別是什么呢?稍微分析一下ImageDescriptor的這兩種創建方式的實現,我們就可以看出來差別:

    1、 createImage每次都創建一個全新的圖片資源(圖片資源的創建是很耗時的~_~

    2、 createResource的方式采用了緩存的方式復用已經創建過的資源,并不是每次都創建一個全新的資源。這一點雖然帶來了性能的提高,但是并沒有解決圖片資源釋放的問題,倒是給開發者留下了一種假象,造成了隨便使用ImageDescriptor的問題,反而造成了大量的圖片資源(當然,更多的是由于調用createImage的方式造成的,因為每次都創建一個全新的圖片資源)沒有釋放。

    到現在為止,我們看到JFace已經對SWT中的圖片資源的管理做了一個小的補充:提供了ImageDescriptor.createResource的方式,可以利用緩存效果,能夠減少不必要的圖片系統資源創建,而且效率有所提高。關于如何釋放,可以參考SWT中覆寫Component.dispose的方式,例如在label provider使用的圖片資源,可以覆寫對于providerdispose方法,JFace框架會自動調用。

          

    JFace中圖片資源管理--ImageRegistry & ResourceManager

    下面,我們接著看一下JFace中的ImageRegistry的實現原理。

    首先我們看一下JFace中的資源管理門面類(façade classJFaceResources,我們由它來獲取我們的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;

    }

    分析了一下ResourceManagerDeviceResourceManager)的實現,我們發現:DeviceResourceManager就是對DeviceResourceDescriptorImageDescriptor)進行了引用計數管理。通過JFaceResources.getResources利用了前面說的Displayhook銷毀機制(注意,如果不通過JFaceResources.getResources來獲取ResourceManager,則不會默認享受Displayhook銷毀機制,需要自己向Display注冊),確保由被托管ImageDescriptor創建的殘留在系統中的圖片資源在Display release的時候會被徹底銷毀。核心方法如下:

    create(DeviceResourceDescriptor descriptor)  

    //如果是首次注冊,創建引用技數,allocate資源并對資源進行緩存

    //如果是已經注冊,增加引用技數,直接返回緩存的系統資源

           destroy(DeviceResourceDescriptor descriptor) //

           //如果引用技術==1,通過調用deallocate徹底銷毀資源

           //如果引用技術>1,削減引用計數(系統資源不會被銷毀)

                 

    那就是說,如果一個ImageDescriptorResourceManager托管了,那由它創建的資源(注意:通過ImageDescriptor.createResource的方式)由兩種銷毀的途徑:

    1、            如果不通過JFaceResources.getResources的方式,單獨使用ResourceManager,則只能利用ResourceManager的引用計數管理來銷毀資源(引用計數為0時),通過顯示調用ResourceManager.destroy來削減引用計數。

    2、            如果通過JFaceResources.getResources來使用ResourceManager,則除了能夠使用到引用計數管理資源,同時也默認使用了Displayhook銷毀機制,JFaceImageRegistry也很好的利用了這一點。

    現在回頭看一下ImageRegistry提供的核心操作,著重分析一下ImageRegistry在利用了ResourceManagerImageDescriptor進行管理的基礎上,做了那些補充:

    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包裝進自己的OriginalImageDescriptorImageRegistry的一個內部類,繼承自ImageDescriptor,對圖片資源本身增加引用計數實現中,并對image本身進行了引用計數管理。同時,對這種方式注冊的圖片資源的銷毀是ImageRegistry自己承擔的,在自身的dispose方法中完成。(注意,在ImageRegistry的構造方法中,將ImageRegistry.dispose封裝為一個runnable注冊到了ResourceManagedispose過程中,而ResourceManage.dispose已經在JFaceResources.getResources方法中被hook到了Display的資源銷毀過程中)。

    3、 通過12的結論,JFace ImageRegistry對系統資源的銷毀已經做了兩手準備,

    其并不希望用戶自己來銷毀資源(無論是通過Image.dispose還是ImageDescriptor.destoryResource,或者ImageRegistry.dispose),當然,ImageRegistry允許通過remove接口來取消注冊。

                 

    JFaceResources

    +提供hook機制

    ImageRegistry

    +自己管理部分資源

    ResourceManager

    +管理ImageDescriptor及其創建的資源

            

    ImageRegistry的適用場景和使用規則】

    通過上面的實現原理分析,我們知道ImageRegistry并不歡迎用戶來過多地參與圖片資源的釋放過程,所以ImageRegistry適用于如下場景:

    1、 決定共享和高度復用的圖片資源。這種資源一般是被使用的特別頻繁,同時,不急于銷毀,只要在Display release的時候銷毀掉就可以了,所以既可以利用到圖片資源本身緩存的優勢(減少物理創建的次數),又可以利用其Displayhook銷毀機制,確保會被銷毀。

    2、 用戶可以直接使用ImageRegistry(不通過JFaceResources.getImageRegistry的方式使用),復用部分ImageRegistry的管理功能,開發自己的緩存策略,但是,要確保自己會在合適的地方調用ImageRegistry.dispose方法來銷毀registryEclipse Workbench中的shared images機制就用了這一點。

    ImageRegistry的使用規則如下:

    1、 誰創建,誰負責。具體圖片資源的創建是由ImageRegistry負責的,用戶既然托管了,就不應該再干預資源的釋放。而且,注冊進ImageRegistry的資源是共享的,一個用戶釋放了,會影響到其他用戶的使用。當然,對于比較熟悉JFace ImageRegistry原理的開發者,可以參與到引用計數的管理,通過這種方式,以安全的、不影響其他用戶使用的方式來間接參與釋放的過程。

    2、 非共享圖片資源請不要交由ImageRegistry托管。對于一個僅限于局部使用而且使用并不是十分頻繁的圖片資源,這樣做不會帶來什么好處,而且,尤其是對于不能參與到引用計數管理的初級用戶,這樣做反而會使得一個本可以馬上釋放的圖片資源反而會一直占用,直到Display release的時候才銷毀。

    3、 要投入精力對ImageRegistrykey值進行管理,否則,會引起混亂。因為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方法等等,這對于一些適用于局部的圖片資源較為適合;當然,也可以變態利用Displayhook釋放機制(但是,一般對于長期使用的資源才會這樣做!!!)。

    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那套東東,引來不必要的負責度和復用,尤其是對于新手來說。

    PS:文章是昨天趕出來的,沒有細看。有什么錯誤之處,歡迎大家指出~_~
       
    附件是本文的word文檔  word格式文檔



    本博客中的所有文章、隨筆除了標題中含有引用或者轉載字樣的,其他均為原創。轉載請注明出處,謝謝!

    posted on 2008-08-06 10:30 zhuxing 閱讀(3182) 評論(3)  編輯  收藏 所屬分類: Eclipse Plug-in & OSGI

    評論

    # re: 【原創】Eclipse插件開發:Eclipse中的圖片資源管理  回復  更多評論   

    thanks for sharing
    2008-08-07 10:05 | hw

    # re: 【原創】Eclipse插件開發:Eclipse中的圖片資源管理  回復  更多評論   

    老大 你知道 我導出的RCP項目后,因為圖片問題啟動不了的原因嗎


    java.lang.Exception: Unsupported or unrecognized format
    at cn.edu.jfcs.app.Application.run(Application.java:51)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.eclipse.equinox.internal.app.EclipseAppContainer.callMethod(EclipseAppContainer.java:572)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:171)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:106)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:76)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:363)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:176)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:508)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:447)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1173)


    我用的是CacheImage.java 主要是這樣得到圖片
    image = AbstractUIPlugin.imageDescriptorFromPlugin(applicationID, imageName).createImage();

    上面問題如何解決啊 有人說要用ImageRegistry才行 不太會用
    2008-08-19 17:19 | appleq

    # re: 【原創】Eclipse插件開發:Eclipse中的圖片資源管理  回復  更多評論   

    createImage和createResource都是調用的一個方法。沒看出createResource是用的緩存機制呢
    2014-06-30 11:10 | wcy
    主站蜘蛛池模板: 免费看男女下面日出水视频| 亚洲AV日韩精品久久久久久久 | 亚洲aⅴ无码专区在线观看春色| 凹凸精品视频分类国产品免费| 成全视频高清免费观看电视剧| 亚洲小说区图片区| 免费国产a国产片高清网站| 日韩精品在线免费观看| 亚洲三级在线观看| 久久久青草青青国产亚洲免观| 亚洲大片免费观看| 性生大片视频免费观看一级| 亚洲精品视频在线免费| 亚洲国产91精品无码专区| **一级毛片免费完整视| 无码人妻一区二区三区免费视频| 亚洲伊人色一综合网| 亚洲乱码中文字幕综合234| 我的小后妈韩剧在线看免费高清版| 五月天国产成人AV免费观看| 亚洲六月丁香六月婷婷蜜芽| 国产亚洲AV手机在线观看| 91免费资源网站入口| 97人妻精品全国免费视频| 国产亚洲一卡2卡3卡4卡新区| 久久精品国产亚洲AV无码麻豆| 亚洲精品tv久久久久| 免费人成在线视频| 免费无码成人AV在线播放不卡| 一本久久A久久免费精品不卡| 亚洲午夜福利在线视频| 亚洲国产精品久久66| 亚洲欧洲日产国码高潮αv| 处破痛哭A√18成年片免费| 99久久人妻精品免费二区| 精品人妻系列无码人妻免费视频| 亚洲人成网站18禁止| 亚洲午夜久久久精品电影院| 亚洲产国偷V产偷V自拍色戒| 亚洲国产成人精品无码久久久久久综合| 免费福利在线播放|