在Android加載圖片時(shí),我們常常使用BitMap,但由于Android對(duì)單個(gè)應(yīng)用施加的內(nèi)存限制,常導(dǎo)致我們加載BitMap時(shí)容易出現(xiàn)內(nèi)存泄漏并導(dǎo)致:java.lang.OutofMemoryError:bitmap size exceedsVM budget
如何解決?
- 高效、按需加載圖片
開始之前,先對(duì)BitMap的加載做一個(gè)簡(jiǎn)單回顧(介紹)
BitMap在Android中指的是一張圖片,可以是png等任何常見的圖片格式
BitMapFactory為我們提供了4類加載方法
- decodeFile 從文件系統(tǒng)加載
- decodeResource 從資源加載
- decodeStream 從輸入流加載
- decodeByteArray 從字節(jié)數(shù)組加載
采樣率 inSampleSize
很多時(shí)候,ImageView本身加載不完一個(gè)圖片的原始大小,于是我們加載整張圖片就是在浪費(fèi)資源。優(yōu)化方案就是通過更改 BitmapFactory.Options.inSampleSize (采樣率)對(duì)圖片進(jìn)行采樣縮放,將縮放后的圖片展示在ImageView上。
當(dāng)inSampleSize為1時(shí),采樣后的圖片大小為原始圖片大?。ú蛔儯?;
當(dāng)inSampleSize大于1,如 2 時(shí),采樣后的圖片寬高均為原來的 1/2 。像素?cái)?shù)自然變?yōu)樵瓉砹?1/4,自然其內(nèi)存占用也為原來的 1/4
舉個(gè)栗子
一張1024 * 1024像素的圖片,采用ARGB8888格式存儲(chǔ),內(nèi)存占有率就為1024 * 1024 * 4(4MB),如果將 inSampleSize 調(diào)整為2,采樣后的圖片像素變?yōu)?512 * 512,內(nèi)存變?yōu)?512 * 512 * 4(1MB)
發(fā)現(xiàn):(像素、內(nèi)存占用)縮放比例均變?yōu)樵瓉淼?1/(inSampleSize^2)
另外
官方指出:inSampleSize的取值應(yīng)為 2 的指數(shù),如果傳的不是 2 的指數(shù),系統(tǒng)將向下選擇一個(gè)最近的2的指數(shù)作值(實(shí)驗(yàn)證明:當(dāng)作建議即可)
步驟
- 將BitmapFactory.Options的
inJustDecodeBounds參數(shù)設(shè)置為true并加載圖片 - 從BitmapFactory.options中取出圖片的原始寬高信息,對(duì)應(yīng)
outWidth和outHeight - 根據(jù)采樣率規(guī)則并結(jié)合目標(biāo)View所需大小計(jì)算出采樣率
inSampleSize - 將BitmapFactory.options 的
inJustDecodeBounds設(shè)置為false,然后重新加載圖片
代碼
public static Bitmap decodeSampleBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
//根據(jù)目標(biāo)尺寸計(jì)算采樣率
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
//長(zhǎng)寬都大于目標(biāo)時(shí),提高采樣率(縮小圖片)
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
//不??s放,直到長(zhǎng)寬中任意一個(gè)再縮就小于目標(biāo)為止
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
//使用
mImageView.setImageBitmap(decodeSampleBitmapFromResource(,R.id.myimage,100,100));
注意:當(dāng)inJustDecodeBounds為true時(shí),BitmapFactory只會(huì)解析圖的原始寬/高信息,并不會(huì)真的獲取圖片,所以這個(gè)操作很輕量。另外這時(shí)候獲取到的寬高信息與圖片的位置以及程序運(yùn)行的設(shè)備有關(guān),同一張drawable目錄下的圖片在不同屏幕密度的設(shè)備上可能得到不同的結(jié)果。這與Android資源加載機(jī)制有關(guān),這一點(diǎn)平時(shí)開發(fā)的時(shí)候應(yīng)該也注意到了。