在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)化方案就是通過(guò)更改 BitmapFactory.Options.inSampleSize (采樣率)對(duì)圖片進(jìn)行采樣縮放齿风,將縮放后的圖片展示在ImageView上。
當(dāng)inSampleSize為1時(shí)绑洛,采樣后的圖片大小為原始圖片大芯劝摺(不變);
當(dāng)inSampleSize大于1真屯,如 2 時(shí)脸候,采樣后的圖片寬高均為原來(lái)的 1/2 。像素?cái)?shù)自然變?yōu)樵瓉?lái)了 1/4,自然其內(nèi)存占用也為原來(lá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)樵瓉?lái)的 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;
//不停縮放矫限,直到長(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)該也注意到了。