Android對單個應(yīng)用的內(nèi)存分配是有限制的,比如16M,這樣就導(dǎo)致在加載Bitmap時很容易就導(dǎo)致OOM(out of menory 內(nèi)存溢出)。
內(nèi)存溢出(out of memory)通俗理解就是內(nèi)存不夠,就是所需要的內(nèi)存達到或超出系統(tǒng)為應(yīng)用分配的大小。
為了解決Bitmap對內(nèi)存造成的困擾,我們需要比較高效的加載Bitmap的策略。
對于Bitmap高效加載,我們先談?wù)?strong>Bitmap的加載。
Bitmap在Android中指的是一張圖片。加載一張圖片我們使用的是Android中 BitmapFactory 類中的方法。BitmapFactory中提供了四類方法:decodeFile,decodeResoure,decodeStream,decodeByteArry。分別可以從文件系統(tǒng),資源,輸入流,及字節(jié)數(shù)組中加載一個Bitmap對象。其中decodeFile,decodeResoure又間接調(diào)用decodeStream方法。這四類方法最終是在Android的底層實現(xiàn)的,對應(yīng)著BitmapFactory類的幾個native方法。
再來談?wù)劯咝Ъ虞d圖片,核心思想就是改變Bitmap的采樣率--inSampleSize。因為我們很多時候顯示的圖片并不需要加載圖片的原始尺寸,我們只需要將圖片按一定比例縮小后顯示即可。這樣在一定程度上降低加載圖片的內(nèi)存,減少了OOM的風(fēng)險。
高效加載Bitmap的一般步驟如下:
1.將BitmapFactory.options的inJustDecodeBonds參數(shù)設(shè)置為true。
2.從BitmapFactory.options中取出圖片的原始寬高信息。
3.根據(jù)目標View的大小結(jié)合原始寬高信息得出相對應(yīng)的采樣率,并設(shè)置還給BitmapFactory.options的inSampleSize。
4.將BitmapFactory.options的inJustDecodeBonds參數(shù)設(shè)置為true。并重新加載圖片。
inJustDecodeBonds參數(shù),設(shè)置為true時, BitmapFactory只會加載 圖片的原始寬高信息,并不會真正加載圖片,所以這個操作是輕量級的。
注意:BitmapFactory獲取的圖片寬高信息和圖片的位置以及程序運行的設(shè)備有關(guān),比如同一張圖片放在不同的drawable目錄下或者程序運行在不同屏幕密度的設(shè)備上,都可能導(dǎo)致BitmapFactory獲取到不同的結(jié)果,和Android的資源加載機制有關(guān)。
inSampleSize,即采樣率,通過對 inSampleSize的設(shè)置,對圖片的像素的高和寬進行縮放。
當inSampleSize=1,即采樣后的圖片大小為圖片的原始大小。小于1,也按照1來計算。 當inSampleSize>1,即采樣后的圖片將會縮小,縮放比例為1/(inSampleSize的二次方)。inSampleSize的取值應(yīng)該總是2的指數(shù),如1,2,4,8等。如果外界傳入的inSampleSize的值不為2的指數(shù),那么系統(tǒng)會向下取整并選擇一個最接近2的指數(shù)來代替。比如3,系統(tǒng)會選擇2來代替(開發(fā)建議)。
注意:通常需要根據(jù)圖片寬高實際的大小/需要的寬高大小,分別計算出寬和高的縮放比。但應(yīng)該取其中最小的縮放比,避免縮放圖片太小,到達指定控件中不能鋪滿,需要拉伸從而導(dǎo)致模糊。
下面是對上面四個步驟的實現(xiàn):
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//加載圖片
BitmapFactory.decodeResource(res,resId,options);
//計算縮放比
options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth);
//重新加載圖片
options.inJustDecodeBounds =false;
return BitmapFactory.decodeResource(res,resId,options);
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) {
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
if(height>reqHeight||width>reqWidth){
int halfHeight = height/2;
int halfWidth = width/2;
//計算縮放比,是2的指數(shù)
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
到這里就可以方便的加載縮放后的圖片了:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.bitmap,100,100);