開源框架KImageLoader開發及原理剖析(二)

開源框架KImageLoader的實現原理與大部分的圖片加載器一樣,內部採用線程池+二級緩存架構,並且儘量做到可配置,可替換。

KImageLoader開源框架Github地址:https://github.com/dolpphins/KImageLoader


KImageLoader整體架構圖

這裏寫圖片描述


KImageLoader類圖

這裏寫圖片描述


KImageLoader關鍵類


ImageLoader: ImageLoader採用了單例模式,也就是應用中有且僅有一個實例。爲了使程序結構更加清晰和提高可擴展性,ImageLoader只是起到轉發的作用,在ImageLoader中並沒有實現過多的業務處理邏輯。它只是把提交過來的任務打包成一個ImageLoadTask對象,然後轉發給ImageLoaderAssistant。而且如果發現調用的是接收一個ImageView的displayImage方法,它會先將一個ImageView對象轉化爲一個displayImage列表,這使得ImageLoader的一對一、一對多的加載模式可以統一起來。

ImageLoaderAssistant: 協助ImageLoader處理相關的邏輯,比如回調監聽接口,提交任務給圖片加載執行器。

ImageLoaderExecutor: 圖片加載執行器,內部維護了一個用於圖片加載的線程池,該線程池採用PriorityBlockingQueue作爲線程隊列,因此客戶端代碼可以指定其任務的優先級以調整任務的執行優先次序,隊列長度爲128,進隊列時如果發現隊列已滿則等待知道隊列不爲滿爲止。由於在線程執行完需要進行相關操作(設置圖片等),所以execute的是一個Future而不是一個Runnable,而且需要把Future存起來,以便待會可以通過它拿到線程執行後得到結果,而且也可以防止任務的重複提交。

ImageLoadActualizer: 任務真正的執行者,它會根據提交的任務的選項採取相關的操作,比如設置允許圖片緩存在內存中,那麼它在獲取到圖片後就會將圖片緩存到內存中,磁盤緩存也一樣。而具體的內存緩存算法,磁盤緩存算法,圖片下載處理都是配置的對應的管理器實現的,ImageLoadActualizer類並不關心。KImageLoader爲內存緩存、磁盤緩存和下載器提供了默認的實現類,用戶代碼也可以自行指定特定的管理器。

ICache: ICache是所有緩存的公共接口,它提供了緩存的相關公共操作接口方法,比如典型的put和get方法。而DiskCache和MemoryCache兩個接口都繼承自ICache接口,分別代表內存緩存和磁盤緩存,它們各自提供了相應的相關接口方法。

Downloader: 下載接口,所有下載器都必須實現該接口。


KImageLoader緩存類


上面說到KImageLoader提供了默認的內存緩存和磁盤緩存實現類,它們分別採用Android中的LruCache類和開源項目DiskLruCache實現。

內存緩存: 在KImageLoader中對應的就是LruMemoryCache類,從名字就可以看出是採用了LRU算法進行緩存管理的,該類使用v4包下的LruCache實現,注意這個與android.util包下的LruCache類是有區別的,可以查看情景學習Android中的LruCache這篇文章。LruMemoryCache類中指定內存緩存的最大大小爲虛擬機堆的最大大小的1/6:

    /** 最大緩存大小,單位:字節,默認爲堆最大大小的1/6*/
    private final static int MAX_CACHE_SIZE = (int) (Runtime.getRuntime().maxMemory() / 6);

    private static android.support.v4.util.LruCache<String, Bitmap> sBitmapCache = new android.support.v4.util.LruCache<String, Bitmap>(MAX_CACHE_SIZE){

        @Override
        protected int sizeOf(String key, Bitmap value) {
            return BitmapUtils.sizeOfBitmap(value);
        }
    };

爲了防止OOM,在put的時候會檢查可用內存大小,內存大於一定的值纔會將圖片放入緩存中:

    @Override
    public boolean put(String key, Bitmap value) {
        if(TextUtils.isEmpty(key) || value == null) {
            return false;
        } else {
            //判斷是否有足夠的內存
            if(checkRemainderMemory(value)) {
                sBitmapCache.put(key, value);
                return true;
            } else {
                return false;
            }
        }
    }

由於sBitmapCache被聲明爲static的,這使得它的生命週期跟應用一樣長,在緩存不使用時爲了避免佔用內存,應該及時將其移除掉,LruMemoryCache提供了clear方法用於移除所有的緩存。

磁盤緩存: 對應KImageLoader中的BitmapDiskLruCache類,採用開源項目DiskLruCache實現LRU算法的磁盤緩存。


相關優化細節


1.從網絡上下載圖片時,並不是直接下載到內存,而是採取複製流數據到磁盤中的方法進行下載,因爲如果要下載的圖片很大,直接下載到內存中可能會導致OOM。下載到磁盤上然後再按一定的質量decode一張Bitmap,可以有效地防止OOM的發生。

2.每次提交任務時都會檢查是否已經存在相同的任務了,如果時就直接返回。這個在AbsListView的使用過程中會經常出現(一般都是getView被調用多次造成的),至於如果判斷兩個任務是否是相同的,可以查看ImageLoadTask類重寫的equals方法和hashCode方法。

3.上面提到,爲了避免OOM,在添加到內存緩存時,會進行可用內存計算,如果這時內存足夠纔會把bitmap緩存到內存中,也就是說即使設置了內存緩存,也不一定100%會被緩存到內存中。

4.使用OkHttp進行網絡下載圖片,OkHttp具有連接複用,自動重試,gzip壓縮等優點。

5.優化LruCache進行內存緩存管理,當內存不足時需要移除的Item數大於maxRemoveCount時不進行移除。

轉載請註明原文地址:http://blog.csdn.net/u012619640/article/details/50554996

本博客已停止更新,轉移到微信公衆號上寫文章,歡迎關注:Android進階驛站
Android進階驛站

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章