Bitmap的加載和Cache

目前比較常用的緩存策略是LruCache(Android3.1提供)和DiskLruCache(是官方文檔推薦酬蹋,但不屬于Android SDK,需要自行下載源碼編譯)嗦哆。

下載地址: https://android.googlesource.com/platform/libcore/+/android-4.1.1-r1/luni/src/main/java/libcore/io/DiskLruCache.java俊犯。

LruCache常被用作內(nèi)存緩存帽衙,而DiskLruCache常被用作磁盤緩存。Lru是Least Recently Used的縮寫纸巷,

LurCache內(nèi)部其實(shí)是用了一個LinkedHashMap來存儲數(shù)據(jù)的镇草,它的構(gòu)造如下:

public LruCache(int maxSize) {

if (maxSize <= 0) {

throw new IllegalArgumentException("maxSize <= 0");

}

this.maxSize = maxSize;

this.map = new LinkedHashMap(0, 0.75f, true);

}

構(gòu)造了一個初始容量為0,負(fù)載因子為0.75瘤旨,accessOrder為true的LinkedHashMap梯啤。accessOrder為true意味著鏈表中元素的順序?yàn)樵L問順序,即調(diào)用get方法后存哲,會將這次訪問的元素移至鏈表尾部条辟,這樣最前面的一個元素就是最近最少使用的了黔夭。當(dāng)元素?cái)?shù)量達(dá)到指定的最大數(shù)量之后是怎么刪除的呢?主要是LinkedHashMap中的removeEldestEntry方法羽嫡。例如可以這樣重寫這個方法:

final int MAX_ENTRIES = 50;

protected boolean removeEldestEntry(Map.Entry eldest) {

return size() > MAX_ENTRIES;

}

這樣當(dāng)put新元素的時(shí)候本姥,如果removeEldestEntry返回了true,就會刪除最老的那個元素杭棵。而返回true 的條件就是LinkedHashMap的size大于我們指定的值婚惫。這就是LruCache的實(shí)現(xiàn)原理。DiskLruCache類似魂爪。

下面開始將如何高效的加載Bitmap先舷。

Bitmap 的加載主要是通過BItmapFactory,BitmapFactory有4中方法加載Bitmap:decodeFile,decodeResource,decodeStream和decodeByteArray滓侍。其中decodeFile蒋川,decodeResource間接調(diào)用了decodeStream方法。

很多時(shí)候我們圖片的大小是大于ImageView的大小的撩笆,這個時(shí)候我們就需要對Bitmap進(jìn)行縮放捺球,如何縮放呢?

主要是通過采樣率來進(jìn)行縮放夕冲。設(shè)置采樣率的方式為 BitmapFactory.Options的inSampleSize參數(shù)氮兵。當(dāng)inSampleSize為1時(shí),表示是圖片的原始大小不縮放歹鱼,當(dāng)inSampleSize大于1泣栈,比如為2時(shí),那么采樣后的圖片的寬帶都為原來的1/2弥姻,而像素?cái)?shù)為原圖的1/4南片,其占有的內(nèi)存大小也為1/4。而且采樣率必須為大于1的整數(shù)才有效果庭敦,當(dāng)小于1時(shí)铃绒,其作用相當(dāng)于1,無縮放效果螺捐。另外最新的官方文檔中指出颠悬,inSampleSize的取值應(yīng)該總是2的指數(shù),比如定血,1,2,4,8,16等等赔癌。如果外界傳遞給系統(tǒng)的inSampleSize不為2的指數(shù),那么系統(tǒng)會向下取整并選擇一個最接近2的指數(shù)來代替澜沟。比如傳3灾票,系統(tǒng)會取2來代替。但是通過驗(yàn)證茫虽,這個結(jié)論并非在所有的Android版本都適用刊苍,建議開發(fā)的時(shí)候按2的指數(shù)取既们。

通過采樣率加載可以按照以下步驟:

1.將BitmapFactory.Options的inJustDecodeBounds參數(shù)設(shè)置為true并加載圖片。(設(shè)置為true之后并不會真正的去加載圖片正什,只會解析圖片的原始寬高啥纸。這個操作是輕量級的,但是得注意這個操作獲取的圖片的寬高信息跟圖片的位置以及程序運(yùn)行的設(shè)備有關(guān)婴氮,比如放在不同的Drawable下面或運(yùn)行在不同分辨率的機(jī)器上)斯棒。

2.從BitmapFactory.Options中取出圖片的原始寬高信息,它們對應(yīng)于outWidth和outHeight參數(shù)主经。

3.根據(jù)采樣率的規(guī)則并結(jié)合目標(biāo)View的所需大小計(jì)算出采樣率inSampleSize荣暮。

4.將BitmapFactory.Options的inJustDecodeBounds參數(shù)設(shè)為false,然后重新加載圖片罩驻。

下面給出一個比較通用的計(jì)算采樣率的算法:

public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){

final int height = options.outHeight;

final int width = options.outWidth;

int inSampleSize=1;

if(height > reqHeight || width > reqWidth){

final int halfHeight = height /2;

final int halfWidth = width / 2;

while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) > = reqWidth){

inSampleSize *=2;

}

}

return inSampleSize;

}

傳入的options為解析后的options穗酥,其中這個算法的關(guān)鍵部分可以仔細(xì)體會一下。

LruCache的典型初始化代碼:

int maxMemory = (int) ((Runtime.getRuntime().maxMemory()) / 1024);

int cacheSize = maxMemory / 8;

mMemoryCache = new LruCache(cacheSize){

@Override

protected int sizeOf(String key, Bitmap value) {

return value.getRowBytes() * value.getHeight() / 1024;

}

};

sizeOf方法是計(jì)算緩存對象大小的惠遏。這里大小需要和總?cè)萘康膯挝灰恢吕荆厦娴臑镵B。

DiskLruCache提供了open方法來創(chuàng)建自身爽哎,如下所示:

public static DiskLruCache open(File dir, int appVersion, int valueCount, long maxSize);

dir表示緩存的文件的目錄蜓席。

appVersion表示應(yīng)用的版本號器一,一般設(shè)為1课锌,版本號發(fā)生改變時(shí),DiskLruCache會清空之前所有的緩存文件祈秕。

valueCount 表示耽擱節(jié)點(diǎn)所對應(yīng)的數(shù)據(jù)的個數(shù)渺贤,一般設(shè)為1即可。

maxSize表示緩存的最大值请毛,比如50MB志鞍,但是要轉(zhuǎn)換成byte。

DiskLruCache緩存的添加:

DiskLruCache的緩存的添加時(shí)通過Editor完成的方仿,Editor表示一個緩存對象的編輯對象固棚。

例如:

DiskLruCache.Editor editor = mDiskLruCache.edit(key);

if(editor != null){

OutputStream outputStream = editor.newOutputSream(index);

}

因?yàn)榍懊嬖O(shè)置了valueCount為1,這里index可以直接傳0仙蚜;

拿到這個outputSream之后此洲,就可以把從網(wǎng)絡(luò)上下載下路的文件流寫入里面,注意最后寫完了委粉,要調(diào)一下editor的commit方法呜师,即editor.commit();

DiskLruCache緩存的查找:

Bitmap bitmap = null;

DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);

if(snapShot != null){

FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(index);

FileDescriptor fileDescriptor = fileInputStream.getFD();

bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);

}

之所以通過fileDescriptor是因?yàn)椋ㄟ^采樣率來縮放圖片之后贾节,會對FileInputStream的縮放存在問題汁汗,因?yàn)镕ileInputStream是一種有序的文件流衷畦,而兩次decodeStream調(diào)用影響了文件流的位置屬性,導(dǎo)致了第二次decodeStream時(shí)得到的是null知牌;所以這里用文件描述符來解決祈争。

mDiskLruCache.remove(key):刪除某個文件。

mDiskLruCache.delete():刪除所有緩存文件送爸。

其他方法可自行查閱铛嘱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市袭厂,隨后出現(xiàn)的幾起案子墨吓,更是在濱河造成了極大的恐慌,老刑警劉巖纹磺,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帖烘,死亡現(xiàn)場離奇詭異,居然都是意外死亡橄杨,警方通過查閱死者的電腦和手機(jī)秘症,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來式矫,“玉大人乡摹,你說我怎么就攤上這事〔勺” “怎么了聪廉?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長故慈。 經(jīng)常有香客問我板熊,道長,這世上最難降的妖魔是什么察绷? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任干签,我火速辦了婚禮,結(jié)果婚禮上拆撼,老公的妹妹穿的比我還像新娘容劳。我一直安慰自己,他們只是感情好闸度,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布竭贩。 她就那樣靜靜地躺著,像睡著了一般筋岛。 火紅的嫁衣襯著肌膚如雪娶视。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機(jī)與錄音肪获,去河邊找鬼寝凌。 笑死,一個胖子當(dāng)著我的面吹牛孝赫,可吹牛的內(nèi)容都是我干的较木。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼青柄,長吁一口氣:“原來是場噩夢啊……” “哼伐债!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起致开,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤峰锁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后双戳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虹蒋,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年飒货,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了魄衅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡塘辅,死狀恐怖晃虫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扣墩,我是刑警寧澤哲银,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站沮榜,受9級特大地震影響盘榨,放射性物質(zhì)發(fā)生泄漏喻粹。R本人自食惡果不足惜蟆融,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望守呜。 院中可真熱鬧型酥,春花似錦、人聲如沸查乒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玛迄。三九已至由境,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虏杰。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工讥蟆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纺阔。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓瘸彤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笛钝。 傳聞我的和親對象是個殘疾皇子质况,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容