
由于bitmap的特殊性以及Android對(duì)應(yīng)用所施加的內(nèi)存限制,導(dǎo)致加載bitmap的時(shí)候很容易出現(xiàn)內(nèi)存溢出。下面這個(gè)異常信息在開發(fā)中應(yīng)該時(shí)常遇到:
java.lang.OutofMemoryError : bitmap size exceeds VM budget
因此高效地加載bitmap是一個(gè)很重要也很容易被開發(fā)者忽視的問題。
1.如何高效加載bitmap
BitmapFactory類提供了四類方法:decodeFile,decodeResource,decodeStream 和 decodeByteArray ,分別用于支持文件系統(tǒng),資源,輸入流,及字節(jié)數(shù)組中加載出一個(gè)Bitmap對(duì)象,其中decodeFile 和 decodeResource 又間接調(diào)用了decodeStream方法,這四類方法最終是在android底層實(shí)現(xiàn)的,對(duì)應(yīng)著BitmapFactory類的幾個(gè)native方法。
如何高效的加載Bitmap ? ?那就是采用BitmapFactory.options來加載所需尺寸的圖片。通過BitmapFactory.Options就可以按一定的采樣率加載縮小后的圖片,將縮小后的圖片在ImageView中顯示,這樣就會(huì)降低內(nèi)存的占用從而在一定程度上避免OOM。
2.什么是采樣率
通過BitmapFactory.Options來縮放圖片,主要是用到了它的inSampleSize參數(shù),即采樣率。當(dāng)inSampleSize為1時(shí),采樣后的圖片大小為原始大小,當(dāng)inSampleSize大于1時(shí),比如2,那么采樣后的圖片其寬高均為原圖大小的1/2,像素?cái)?shù)為原圖的1/4,占有內(nèi)存的大小也為原圖的1/4。有一種特殊情況,當(dāng)inSampleSize小于1時(shí),其作用相當(dāng)于1,即無縮放效果。另外官方文檔指出,inSampleSize的值應(yīng)該總是為2的指數(shù),比如:1,2,4,8,16 等等。如果未見傳遞的inSampleSize 不為2的指數(shù),那么系統(tǒng)會(huì)向下取整并選擇一個(gè)最接近的2的指數(shù)來代替。
3.采樣流程
(1)將BitmapFactory.Options的inJustDecodeBounds參數(shù)設(shè)為true并加載圖片。
(2)從BitmapFactory.Options中取出圖片的原始寬高信息。他們對(duì)應(yīng)于outWidth 和 outHeight 參數(shù)。
(3)根據(jù)采樣率的規(guī)則并結(jié)合目標(biāo)View的所需大小計(jì)算出采樣率inSampleSize.
(4)將BitmapFactory.Options的inJustDecodeBounds 參數(shù)設(shè)為false,然后重新加載圖片。
4.采樣流程實(shí)現(xiàn)程序
public static Bitmap decodeSampledBitmapFromResource(Resources res ,int resId, int ? ? ? ? ? ? ? ?reqWidth, int reqHeight){
? ? final BitmapFactory.Options options = new BitmapFactory.Options();
? ? options.inJustDecodeBounds = true;
? ? BitmapFactory.decodeResource(res, resId, options);
? ? options.inJustDecodeBounds? = false;
? ? return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
? ? final int width = options.outWidth;
? ? final int height = options.outHeight;
? ? int inSampleSize = 1;
? ? if(height > reqHeight || width > reqWidth){
? ? ? ? ?final int halfHeight = height/2;
? ? ? ? ?final int halfWidth = width/2;
? ? ? ? while((halfHeight /inSampleSize) >reqHeight
? ? ? ? ? ? ? ?&& (halfWidth /inSampleSize) >halfWidth){
? ? ? ? ? ? ? inSampleSize *=2;
? ? ? ? ? ? ? }
? ? ? }
? ? ? return inSampleSize;
}
PS: 本文是對(duì)《Android 開發(fā)藝術(shù)探索》一書的閱讀筆記,想了解原文 請(qǐng)自行搜索。