寫在前面的話,本篇文章是參考自《Android開發(fā)藝術探索》所寫,看此書已是2015年的事情啦,由于獨立開放項目,以至于對于Android原理性東西生疏,最近需要換工作,重新?lián)炱鸫藭?,仍有大的收獲。故在此留下一筆。(總結是很有必要的)
在Android圖片加載方面,我們少不了與Bitmap(位圖)打交道,但是與它相處需要步步謹慎啊,稍不留神就跟OOM(內(nèi)存溢出)見面啦。
> java.lang.OutofMemoryError:bitmap size exceeds VM budget
如何高效地加載Bitmap是我們每一個開發(fā)者都不容忽視的問題。
下面我們來逐步地深入探討這個問題:
一:如何加載Bitmap
Bitmap在Android中簡單理解為一張圖片,圖片的格式可以是(JPEG ,PNG ,WEBP等)。
BitmapFactory為我們提供了4類方法來加載Bitmap:
1.decodeFile();
2.decodeResource();
3.decodeStream();
4.decodeByteArray();
看圖更明白一些:

如果深入去查看這些方法,會發(fā)現(xiàn)其中** decodeFile()和decodeResource()都間接調(diào)用了decodeStream()。這四類加載方法最終都是在Android低層實現(xiàn)的,對應著BitmapFactory類的幾個native方法。
到了這里,Bitmap的簡單加載就介紹完畢,但是Bitmap的加載遠沒有結束,因為如果我們這么簡單使用,會經(jīng)常與OOM**邂逅。這就需要進行高效設置啦。
二:如何高效地加載Bitmap
首先來交代一下背景:其實所謂高效,只不過就是不浪費系統(tǒng)那寶貴的資源。在Android開發(fā)中,與Bitmap打交道最多的控件要屬ImageView。在很多情況下,ImageView都沒有圖片的原始尺寸那么大,這時如果我們不加任何設置而直接讓ImageView來顯示圖片。Android系統(tǒng)往往都是先將整個圖片加載到內(nèi)存,然后再顯示出來。這顯然是畫蛇添足。
我們可以通過BitmapFactory.Options來通過設置采樣率來加載所需尺寸的圖片,這樣就降低內(nèi)存占用從而在一定程度上降低了OOM的發(fā)生率。BitmapFactory提供的加載圖片的四類方法都支持BitmapFactory.Options參數(shù),通過它我們就可以很方便地進行采樣縮放。(意不意外,驚不驚喜?)
那么我們是如何通過BitmapFactory.Options來進行采樣縮放的呢?
這里主要用到了inSampleSize這個變量;

從圖上的注釋我們可以知道:
- 如果inSampleSize > 1,將會給我們返回一個比原圖更小的圖片來節(jié)約內(nèi)存。舉個例子,當設置 inSampleSize = 4時,那么采樣后的圖片其寬和高都縮小為原圖的1/4,而像素數(shù)為原圖的1/16,這樣其所占用的內(nèi)存也只有原來的1/16。
- 當 inSampleSize <= 1時,系統(tǒng)都會當成1來對待;
- 這個inSampleSize 的取值應該總為2 的指數(shù)(1,2,4,8,18...),如果外界傳遞給系統(tǒng)的inSampleSize不為2的指數(shù),則系統(tǒng)會向下取整并選擇一個最接近2的指數(shù)來代替。
獲取采樣率的流程:
- 獲取BitmapFactory.Options的對象;
- 將BitmapFactory.Options的inJustDecodeBounds參數(shù)設置為true并加載圖片;
- 從BitmapFactory.Options中取出原始圖片的寬高信息,即outWidth和outHeight參數(shù)。
- 根據(jù)采樣率的規(guī)則并結合目標View的所需大小計算采樣率inSampleSize。
- 將BitmapFactory.Options的inJustDecodeBounds參數(shù)設置為false,然后重新加載圖片。
針對于inJustDecodeBounds這個變量:
將 inJustDecodeBounds參數(shù)設置為ture時,BitmapFactory只會解析圖片的原始寬高信息,并不會真正地加載圖片,所以這個操作是輕量級的。
"Talk is cheap. Show me the code." - Linus Torvalds
// 從res中加載bitmap
public static Bitmap decodeBitmapFromRes(Resources res,int resId,
int requestWidth,
int requestHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res,resId,options);
//設置采樣率
options.inSampleSize = calculateInSampleSize(options,requestWidth,requestHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res,resId,options);
}
//計算采樣率
private static int calculateInSampleSize(BitmapFactory.Options options,
int requestWidth,
int requestHeight) {
int outWidth = options.outWidth;
int outHeight = options.outHeight;
int inSampleSize = 1;
if(outHeight>requestHeight || outWidth > requestWidth){
int halfHeight = outHeight / 2;
int halfWidth = outWidth / 2;
while ((halfHeight/ inSampleSize) >= requestHeight
&& (halfWidth / inSampleSize) >= requestWidth){
inSampleSize *= 2;
}
}
return inSampleSize;
}
到此,Bitmap加載可以在實際開發(fā)工作中使用啦。