Android Bitmap 全面解析(三)開源圖片框架分析1-UIL(上) ...

主要介紹這三個框架,都挺有名的,其他的框架估計也差不多了

Android-Universal-Image-Loader
https://github.com/nostra13/Android-Universal-Image-Loader

ImageLoader
https://github.com/novoda/ImageLoader

Volley(綜合框架,包含圖片部分)
https://github.com/mcxiaoke/android-volley


扯淡時間,可以跳過這段
這些開源框架的源碼還是挺複雜的,本人技術有限,有部分分析不對的地方或者不足的地方希望大家一起討論,
由於有大量的源代碼分析,所以造成內容較多且比較雜亂,重點部分我會用紅字或者加粗字體標出來,如果沒有耐心全部看完的話可以挑部分重點看,可以跳過的部分我也會有說明,大家可以選擇性閱讀
其實框架的實現和我們教程(一)(二)章的也差不多,只不過在此基礎上進行了一些優化,核心部分是幾乎沒有區別的


由於篇幅有限,暫時只介紹第一個框架(其他其實也都差不多),另外兩個在後續教程中詳細介紹


---------------------------------------------------------------------


首先介紹universal-image-loader(以下簡稱UIL)
是github社區上star最多的一個項目,可以理解爲點贊最多滴,應該是最有名的一個~國內很多知名軟件都用它包括淘寶京東聚划算等等
框架其實都差不多,這裏詳細介紹下使用者最多的一個~之後再分析其他框架時就簡單說明一些特性了,重複相似部分不再贅述了~




使用比較簡單,這個框架的github主頁上也有快速使用的步驟
基本上就是在application類裏的oncreate方法(整個程序開始時運行一次)中進行一下簡單的基本配置,
可以根據需要自行進行設定,懶得設定的話框架也提供了一個默認的配置,調用一個方法即可
基本上是配置一些類似於:緩存類型啊,緩存上限值啊,加載圖片的線程池數量啊等等


此外在頁面內顯示的時候還要設置一個顯示配置這個配置不同於基本配置,一個項目裏可以根據需要創建多個配置對象使用,
這個配置就比較具體了,可以設置是否使用disk緩存(存到sd卡里一般),加載圖片失敗時顯示的圖片,默認圖片,圖片的色彩樣式


配置好以後,就是簡單的使用了,創建一個圖片加載對象,然後一行代碼搞定顯示圖片功能~參數一般是入你需要顯示的圖片url和imageview對象
大部分框架其實都這一個尿性,配置稍微麻煩點,但是使用時一般只需要一行,顯示方法一般會提供多個重載方法,支持不同需要~




由於不是框架使用教程,所以~
下面結合之前兩章的內容着重分析下框架對於單張圖片的壓縮處理,和多圖緩存池的處理


---------------------------------------------------------------------


單張圖片的壓縮
(業界良心的小技巧: 框架肯定也是基於android sdk的, 所以獲取圖片縮放實例的話,option的inSampleSize參數是肯定要使用的, 我們直接crtl+h打開搜索頁面,選擇file search, 然後file name patterns選擇*.java,即搜索所有java文件,最後在containing text上輸入想搜索的內容,這裏我們要搜inSampleSize,搜索結果裏隨便掃一掃,發現BaseImageDecoder裏面有個靠譜的方法如下)


01 protected Options prepareDecodingOptions(ImageSize imageSize, ImageDecodingInfo decodingInfo) {
02 ImageScaleType scaleType = decodingInfo.getImageScaleType();
03 int scale;
04 if (scaleType == ImageScaleType. NONE) {
05 scale = ImageSizeUtils. computeMinImageSampleSize(imageSize);
06 } else {
07 ImageSize targetSize = decodingInfo.getTargetSize();
08 boolean powerOf2 = scaleType == ImageScaleType.IN_SAMPLE_POWER_OF_2 ;
09 scale = ImageSizeUtils. computeImageSampleSize(imageSize, targetSize, decodingInfo.getViewScaleType(), powerOf2);
10 }
11 if (scale > 1 && loggingEnabled) {
12 L. d(LOG_SUBSAMPLE_IMAGE, imageSize, imageSize.scaleDown(scale), scale, decodingInfo.getImageKey());
13 }
14  
15 Options decodingOptions = decodingInfo.getDecodingOptions();
16 decodingOptions. inSampleSize = scale;
17 return decodingOptions;
18 }



簡單掃一眼,ImageSize,ImageDecodingInfo神馬的明顯是自定義的一個類,不要管,我們先挑重點部分看
Options decodingOptions = decodingInfo.getDecodingOptions();
decodingOptions.inSampleSize= scale;

方法最後兩行可以看出來ImageDecodingInfo類裏面保存了一個option對象~
通過一個方法對其中的inSampleSize進行了設置~


ImageScaleType.NONE 什麼意思,掃了眼註釋,是圖片無壓縮~那我們看else裏面的需要壓縮的computeImageSampleSize方法

方法是具體如何處理的呢~ 我們再繼續跟蹤computeImageSampleSize方法~
(業界良心小技巧:按着ctrl不鬆左鍵點擊方法或者變量或者類,就可以自動跳轉到對應的地方了)


方法的代碼如下


01 /**
02 * Computes sample size for downscaling image size (<b> srcSize</b> ) to view size (<b>targetSize</b> ). This sample
03 * size is used during
04 * {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)
05 * decoding image} to bitmap.<br />
06 * <br />
07 * <b>Examples: </b><br />
08 * <p/>
09 * <pre>
10 * srcSize(100x100), targetSize(10x10), powerOf2Scale = true -> sampleSize = 8
11 * srcSize(100x100), targetSize(10x10), powerOf2Scale = false -> sampleSize = 10
12 *
13 * srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> sampleSize = 5
14 * srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> sampleSize = 2
15 * </pre>
16 * <p/>
17 * <br />
18 * The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded
19 * bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16
20 * the number of pixels. Any value <= 1 is treated the same as 1.
21 *
22 * @param srcSize Original (image) size
23 * @param targetSize Target (view) size
24 * @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
25 * @param powerOf2Scale <i>true </i> - if sample size be a power of 2 (1, 2, 4, 8, ...)
26 * @return Computed sample size
27 */
28 public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
29 boolean powerOf2Scale) {
30 int srcWidth = srcSize.getWidth();
31 int srcHeight = srcSize.getHeight();
32 int targetWidth = targetSize .getWidth();
33 int targetHeight = targetSize .getHeight();
34  
35 int scale = 1;
36  
37 int widthScale = srcWidth / targetWidth;
38 int heightScale = srcHeight / targetHeight;
39  
40 switch (viewScaleType) {
41 case FIT_INSIDE:
42 if (powerOf2Scale) {
43 while (srcWidth / 2 >= targetWidth || srcHeight / 2 >= targetHeight) { // ||
44 srcWidth /= 2;
45 srcHeight /= 2;
46 scale *= 2;
47 }
48 } else {
49 scale = Math. max(widthScale, heightScale); // max
50 }
51 break;
52 case CROP:
53 if (powerOf2Scale) {
54 while (srcWidth / 2 >= targetWidth && srcHeight / 2 >= targetHeight) { // &&
55 srcWidth /= 2;
56 srcHeight /= 2;
57 scale *= 2;
58 }
59 } else {
60 scale = Math. min(widthScale, heightScale); // min
61 }
62 break;
63 }
64  
65 if (scale < 1) {
66 scale = 1;
67 }
68  
69 return scale;
70 }



我連註釋一起復制過來了,裏面對參數有比較詳細的介紹,還有對應的例子~很良心的註釋啊~
可以看到核心計算方法,其實和我們教程(一)裏面是一樣的(區別在於這裏有兩個情況的處理,一個是||一個是&&,後面會介紹原因),對源圖的寬高和目標圖片的寬高進行一些判斷,滿足時一直循環下去進行處理,直到取得一個合適的比例值爲止,最終保證
使壓縮後顯示的圖片像素密度是大於等於設定的像素密度的那個最低值
(這裏換了個更合適的說法,因爲框架對圖片不同scaleType造成的不同顯示效果進行的區別處理,使得壓縮比例值計算的更加精確,其實不用這個優化處理已經能夠滿足圖片縮放顯示需求了,這裏是爲了做到更好,當然也會更麻煩了,所以作爲我們學習者來說要自行取捨要研究到哪個深度更合適,後面有單獨一部分進行介紹,可以選擇性看)


框架進行了優化,添加了一個powerOf2Scale的參數和viewScaleType的參數區別不同情況以進行響應處理,方法註釋裏面都有介紹參數的作用,我這裏也簡單說明下


powerOf2Scale: 是否爲2的冪數,也就是2.4.8.16.....
     true時即我們之前教程裏面舉得例子,也是官方推薦的,最好縮放比例是2的冪數(2的N次方),需要通過循環每次*2的計算壓縮比例
     false即不需要是2的冪數,可以是1.2.3.4...任何一個數,直接用圖片寬高除以所需圖片寬高即可獲得壓縮比例(注意要是整數)
所以,true的時候和我們教程(一)中的算法一致,而false的時候不做限定,那就可以簡單的直接進行除法操作了


viewScaleType: 就是android裏控件imageView的scaleType屬性,這裏集合成了兩種,對應關係見框架的源碼,如下
01 public static ViewScaleType fromImageView(ImageView imageView) {
02 switch (imageView.getScaleType()) {
03 case FIT_CENTER :
04 case FIT_XY :
05 case FIT_START :
06 case FIT_END :
07 case CENTER_INSIDE :
08 return FIT_INSIDE ;
09 case MATRIX :
10 case CENTER :
11 case CENTER_CROP :
12 default:
13 return CROP ;
14 }
15 }


再次重複
第一章的計算方法掌握了就可以了,已經能夠滿足開發需求,下面這段是對於不同viewScaleType的處理分析,可以跳過,有興趣的話就接着看看


---------------------------------------------------------------------

本段可略過~
這部分內容還是很繞的,我也是邊研究邊寫,實例demo也一邊跑一邊debug看研究,基本上算是大概瞭解了
這一段反覆修改了多次,耗費了大量精力,腦子有點迷亂了都,所以可能有不太準確的地方希望大家提醒下,我再做修改~


根據他的兩種不同算法(||和&&),我們舉個具體例子說明下
比如我們限定圖片要200 200~ 原圖是1000 600~ 控件則是100 100的正方形(單位都是像素)
先看默認的FIT_INSIDE效果
第一次循環,1000/2 >= 200 || 600/2 >= 200 都滿足, 源寬高變成一半 500 400,縮放值翻倍,變成2~
第二次循環,500/2 >= 200 || 300/2 >= 200 滿足其中一個, 源寬高變成一半 250 200,縮放值翻倍,變成4~
第三次循環,250/2 >= 200 || 150/2 >= 200 都不滿足,結束循環,最後縮放值爲 4
以縮放比例4計算,獲得的縮放圖片實例的寬高即爲250 150


CROP
效果時的處理
第一次循環,1000/2 >= 200 && 600/2 >= 200 都滿足, 源寬高變成一半 500 400,縮放值翻倍,變成2~
第二次循環,500/2 >= 200 && 300/2 >= 200 只滿足其中一個,沒有滿足&&的條件,結束循環,最後縮放值是2~
以縮放比例2計算,獲得的縮放圖片實例的寬高即爲500 300


這樣看的話,250 150的結果沒有滿足寬高都大於等於限定值200 200的原則啊~
思考下壓縮圖片的原則,首先儘可能的縮小,因爲越小越節約內存,但是不能太小,因爲壓縮過大的圖不夠清楚,效果不好~跟看騎兵電影似得,所以要有個最低值~
比如我的imageview控件的寬高是100 100大小的一個正方形,那我希望將顯示的圖片壓縮成至少爲200 200像素的大小,這樣我控件的單位大小上都可以顯示至少4個像素的圖片點~




所以限定大小,實際上是爲了限定控件上圖片的像素密度,也就是控件單位大小上圖片像素點的數量~
對於控件正方形,圖片幾乎也是正方形的情況,那就簡單了
比如一個500 500的圖片和一個200 200的圖片都放在100 100的imageview上~外觀看上去的大小都是一樣的,但是單位大小上的圖片像素點數量是不同的,500*500=250000,分佈在100*100的控件上,那控件單位大小上就有25個圖片像素點~
200 200的圖片,圖片像素密度則是4~ 肯定是單位大小上圖片像素點越多就越清楚了




爲什麼要有區分處理呢,因爲對於圖片長寬比例和控件長寬比例相差很大時,不同的scaleType造成的顯示縮放效果區別是很大的~
在長寬比相同或者相近時,比如都是正方形,那麼兩種顯示效果毫無區別,但是如果控件是正方形,圖片是3:2甚至3:1的矩形,那差別就明顯了, 我們看下比例值差別較大時的
FIT_INSIDE的顯示效果和CROP_INSIDE


兩種顯示效果簡單的理解爲
     FIT_INSIDE(左圖)   完整的展示圖片,但imageView控件可能不佔滿
     CROP(右圖)           填充整個imageview,圖片可能會被裁去一部分


對於上面的例子1000 600的圖, 而圖片限定大小是200 200
如果兩種效果都是用一種壓縮規則算法,比如官方提供的那種規則(也是我們教程一里面的),則會把圖片壓縮成了500 300~
分析下壓縮後圖片在兩種scaleType下的縮放情況


FIT_INSIDE模式時
顯示時候的縮放(不是圖片像素的壓縮)是根據較長的那個邊縮放的(藍色框框的上下兩條長邊),
500縮放成了100,縮放比例爲5,那像素密度就是25


CROP模式時
顯示時候的縮放是根據較短的那個段邊縮放的(藍色框框的左右倆短邊)
上例中是短邊是300,縮放成了100,比例爲3,那單位大小上圖片像素點就是9


預期大小是200 200,顯示在100 100的控件上其實就相當於希望單位大小上的圖片像素數量4以上




那個25貌似是有點多了呢~
那我們按照UIL框架裏針對FIT_INSIDE的算法重新計算下,壓縮後圖片像素爲250 150
縮放類型按照長邊縮放,比例爲2.5~單位大小上圖片像素點是12.5~明顯滿足條件又更加合適~


那CROP縮放類型也按照UIL框架FIT_INSIDE對應的算法處理呢?
算出來是250 150,顯示在CROP上,則是按照短邊縮放,比例是1.5~最後像素密度是2.25,不到4,明顯是不滿足的了~




所以圖片限定寬高大小,是爲了保證圖片的清晰度,實質上是要保證單位大小上有足夠的像素點,即對像素密度有個最低值要求
根據長邊縮放的FIT_INSIDE效果,我們只需要保證長的邊即其中一個大於等於限定數值即可
而根據短邊縮放的CROP效果,要保證短的邊大於等於限定數值即只有寬高都滿足大於等於限定的數時纔可以
由於大部分情況下,尤其是實際應用場景中,什麼頭像啊,logo啊,圖片啊大部分其實都是控件和圖片長寬比相似的,偶爾有幾個奇葩圖片也浪費不來多少內存的,所以一般沒有做區分處理~UIL框架這種區別計算比例更加的準確,當然,同時也更加難了


---------------------------------------------------------------------


圖片色彩樣式


縮放比例的控制介紹完畢,基本上和我們教程(一)中介紹的方法差不多(沒羞沒臊啊), 只不過進行了一些優化
而對於圖片色彩樣式的控制,則可以在框架提供的顯示配置對象中設置
DisplayImageOptions.bitmapConfig(Bitmap.Config.RGB_565) 傳入所需色樣即可,默認同樣是ARGB_8888


---------------------------------------------------------------------


多張圖片的緩存池


單張圖片的縮放處理其實還是比較簡單的(不考慮不同縮放效果區別處理的話)~
重點在於多張圖片的緩存池控制,下面進行介紹


首先是UIL的內存緩存
一共有八種內存緩存池類型,使用時只要選擇其中的一個即可(通過ImageLoaderConfiguration.memoryCache(...)設置)
我們看下UIL在github上的主頁中對於各個內存緩存類型的具體介紹
(主頁地址見文章最開始處)


灰色框中標註的七種


只用了強引用,是現在官方推薦的方法,相當於我們教程(二)裏面說的只用LruCache一個強引用緩存池的方式~也是框架的默認內存緩存方式
     LruMemoryCache     當超過緩存數量的極限值時刪除使用時間離得最遠的圖片(即LruCache的算法)


同時使用弱引用+強引用,也就是相當於教程(二)裏面說的二級緩存技術,區別在於這裏對其中的一級緩存即強引用緩存池部分的刪除算法進行了細分,採用了不同的規則,看下小括號後面的英文就知道了,我這裏簡單的翻譯下
    UsingFregLimitedMemoryCache 當超過緩存數量的極限值時刪除最不常用的圖片
     LruLimitedMemoryCache           當超過緩存數量的極限值時刪除使用時間離得最遠的圖片(即LruCache的算法)
     FIFOLimitedMemoryCache         當超過緩存數量的極限值時根據FIFO first in first out 先入先出算法刪除
     LargestLimitedMemoryCache     當超過緩存數量的極限值時刪除最大的圖片
     LimitedAgeMemoryCache          當超過緩存數量的極限值時刪除超過存在最長時間的那個圖片(這個翻譯有可能不太準確= = )


只使用弱引用 由於完全沒有使用強引用,所以肯定不會出現OOM異常,但是效率上捉雞~ UIL使用時基本上很少出現OOM異常的,真是人品爆發出現了,那解決辦法之一就是將內存緩存設置成這個只用弱引用的類型,因爲沒有強引用部分~
     WeakmemoryCache     無限制的內存(系統根據弱引用規則自動回收圖片)




估計是框架很早以前就開始開發的問題,強引用部分用的是LinkedHashMap類,沒有采用LruCache類,但實際上處理邏輯基本都是一樣的~由於LinkedHashMap的功能稍微弱一點~教程二里面可以看到我們是通過size()>閥值判斷的,也就是僅根據數量而不是根據所有圖片內存總大小,所以UIL框架中自己做了對應處理,在這個自定義個的強引用類型LruMemoryCache中設置了一個總內存size參數,每次put remove等操作時,都計算size的變化,並且在每次put操作時調用一個trimToSize方法,用於判斷添加後的緩存池大小,觀察size值是否超過了maxSize閥值,超過自定義的內存總大小值時則移除最老的圖片


代碼如下
001 package com.nostra13.universalimageloader.cache.memory.impl;
002  
003 import android.graphics.Bitmap;
004  
005 import com.nostra13.universalimageloader.cache.memory.MemoryCache;
006  
007 import java.util.Collection;
008 import java.util.HashSet;
009 import java.util.LinkedHashMap;
010 import java.util.Map;
011  
012 /**
013 * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
014 * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may
015 * become eligible for garbage collection.<br />
016 * <br />
017 * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
018 *
019 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
020 * @since 1.8.1
021 */
022 public class LruMemoryCache implements MemoryCache {
023  
024 private final LinkedHashMap<String, Bitmap> map;
025  
026 private final int maxSize;
027 /** Size of this cache in bytes */
028 private int size;
029  
030 /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
031 public LruMemoryCache(int maxSize) {
032 if (maxSize <= 0) {
033 throw new IllegalArgumentException("maxSize <= 0");
034 }
035 this.maxSize = maxSize;
036 this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
037 }
038  
039 /**
040 * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
041 * of the queue. This returns null if a Bitmap is not cached.
042 */
043 @Override
044 public final Bitmap get(String key) {
045 if (key == null) {
046 throw new NullPointerException("key == null");
047 }
048  
049 synchronized (this) {
050 return map.get(key);
051 }
052 }
053  
054 /** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */
055 @Override
056 public final boolean put(String key, Bitmap value) {
057 if (key == null || value == null) {
058 throw new NullPointerException("key == null || value == null");
059 }
060  
061 synchronized (this) {
062 size += sizeOf(key, value);
063 Bitmap previous = map.put(key, value);
064 if (previous != null) {
065 size -= sizeOf(key, previous);
066 }
067 }
068  
069 trimToSize(maxSize);
070 return true;
071 }
072  
073 /**
074 * Remove the eldest entries until the total of remaining entries is at or below the requested size.
075 *
076 * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
077 */
078 private void trimToSize(int maxSize) {
079 while (true) {
080 String key;
081 Bitmap value;
082 synchronized (this) {
083 if (size < 0 || (map.isEmpty() && size != 0)) {
084 throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
085 }
086  
087 if (size <= maxSize || map.isEmpty()) {
088 break;
089 }
090  
091 Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
092 if (toEvict == null) {
093 break;
094 }
095 key = toEvict.getKey();
096 value = toEvict.getValue();
097 map.remove(key);
098 size -= sizeOf(key, value);
099 }
100 }
101 }
102  
103 /** Removes the entry for {@code key} if it exists. */
104 @Override
105 public final void remove(String key) {
106 if (key == null) {
107 throw new NullPointerException("key == null");
108 }
109  
110 synchronized (this) {
111 Bitmap previous = map.remove(key);
112 if (previous != null) {
113 size -= sizeOf(key, previous);
114 }
115 }
116 }
117  
118 @Override
119 public Collection<String> keys() {
120 synchronized (this) {
121 return new HashSet<String>(map.keySet());
122 }
123 }
124  
125 @Override
126 public void clear() {
127 trimToSize(-1); // -1 will evict 0-sized elements
128 }
129  
130 /**
131 * Returns the size {@code Bitmap} in bytes.
132 * <p/>
133 * An entry's size must not change while it is in the cache.
134 */
135 private int sizeOf(String key, Bitmap value) {
136 return value.getRowBytes() * value.getHeight();
137 }
138  
139 @Override
140 public synchronized final String toString() {
141 return String.format("LruCache[maxSize=%d]", maxSize);
142 }
143 }




代碼分析
核心方法是put() 和trimToSize()


put方法:
synchronized修飾是爲了防止多線程訪問時造成size計算錯誤的,
sizeOf是自定義方法,計算圖片size大小的,這裏首先將添加的圖片大小增至緩存池總大小size中~
然後map.put方法添加圖片對象,返回值是如果map裏對應key已經有值了,則返回put前已有的那個對象~當然,put以後value已經被替換了,所以在+=新的圖片對象大小後,還要將前一個被替換掉的圖片size從總緩存池內存大小中減去
最後完成添加過程以後調用trimToSize方法查看添加操作完成後當前緩存池大小是否超過我們自定義的閥值總大小maxSize


trimToSize方法:
無限循環直到當前緩存池大小size低於自定義閥值大小maxSize爲止~ 如果沒有滿足size<=maxSize,即當前緩存池過大,則對map添加一個迭代器依次遍歷整個map從始至末挨個刪除,直到滿足size<=maxSize結束循環~ 之所以用循環遍歷,是因爲可能不止刪除一個圖片,比如最早添加的圖片只有5kb,而新圖片有15kb,那隻刪除最早一張圖片是不行的,可能添加進來一張需要擠出去多張~


每次put的時候hashMap都會將新對象添加至末端~key已存在時則替代對應的value對象並移到末端~
而當迭代器遍歷的時候則是從始端開始遍歷的,也就等於個LRU的算法,刪除使用時間距今最老的圖片
則UIL框架自定義的這個強引用LRU緩存類,主要還是對內存閥值size進行了處理和判斷


框架裏的處理和LinkedHashMap的removeEldestEntry方法最終實現的效果都是一樣的~只不過具體代碼實現方式不同而已
這裏使用自定義方法然後進行處理,個人目測應該是爲了支持框架多重緩存池類型而設計的,畢竟LinkedHashMap的removeEldestEntry僅提供刪除最早使用對象的功能,而刪除最大圖片等這樣其他的功能就不支持了~


以上是隻用強引用的緩存池部分,還算簡單,下面是二級緩存


二級緩存(弱+強)
這部分就凌亂了~由於UIL框架中有5個弱+強的具體實現類(詳見上面的內存緩存介紹圖),所以基本功能實現都是在基類中進行的處理,實現的子類中僅針對算法進行了自定義規則而已~ 由於代碼涉及到多層多個類= = 本人的水平又有限,所以...就不仔細研究了,可能後續有時間的時候再補充到帖子裏~


大概思路是基類提供一個虛擬方法removeNext,返回bitmap值,然後在基類裏put方法後判斷強引用的size超過閥值時,對這個方法返回的bitmap對象刪除操作,然後將其移至弱引用池裏~ 具體移除規則則子類中自行編寫,你還可以設計個按圖片size單雙數刪除圖片一類的特殊規則


這就屬於設計思想了,可以參考LinkedHashMap的removeEldestEntry源碼,可以看到源碼中只使用,而方法內則是空的,就是留給開發者自行繼承重寫邏輯的


自己如若有興趣開發框架,可以根據自己階段性的水平來,開始的時候就直接用系統的LruCache寫就可以了,二級緩存也很好處理~




注意:
我們教程裏使用的是軟引用,這裏是弱引用~兩者其實也差不多,弱引用生命週期比軟引用還低點~但是效率其實都一般,官方都不推薦使用了
肯定是要跟着官方權威信息走的,所以UIL框架的默認內存緩存方式用的也是LruMemoryCache類, 但同時又保留了其他類型的緩存類, 框架使用者可以根據實際需要在配置時通過ImageLoaderConfiguration.memoryCache(...)方法設置其他提供的類型緩存


關於軟引用其他那幾個類型算法FIFO等的具體實現,我這裏就不研究了,算法神馬的水平有限,大家有興趣可以自己看看






此外還有一個FuzzyKeyMemoryCache的內存緩存類, UIL中緩存裏是支持保存同一張圖片的多種縮放比例緩存對象的,設置中也可以取消這種設置使得緩存池中一個圖片只保留一個size的緩存對象,通過調用 ImageLoader.denyCacheImageMultipleSizesInMemory()方法實現, 此時,UIL框架就會自動使用Fuzzy...去包裝一下你之前選擇的內存緩存類型~即算法還是按照你選擇的類型,但是多了一個只保持一種size緩存對象的功能,這種設計叫做Decorator裝飾模式,舉個例子幫助理解下,這種設計模式相當於有一個變相怪傑的特殊面具,無論人或狗,誰帶上誰就能具有相應的牛逼能力~
算是一個特殊的內存緩存類型,不能單獨使用




出處http://www.eoeandroid.com/thread-333220-1-1.html
發佈了17 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章