Android高效加載大圖,有效避免程序OOM
相信不少同學(xué)對(duì)OOM都不陌生吧叉讥,大都是因?yàn)閮?nèi)存里加載的東西太多導(dǎo)致內(nèi)存泄漏。
今天我們就來學(xué)習(xí)一下如何高效的加載大圖
上一節(jié)我們講到了調(diào)用系統(tǒng)相冊(cè)的例子,系統(tǒng)圖片庫里展示的圖片大都是用手機(jī)攝像頭拍出來的资铡,這些圖片的分辨率會(huì)比我們手機(jī)屏幕的分辨率高得多。大家應(yīng)該知道幢码,我們編寫的應(yīng)用程序都是有一定內(nèi)存限制的笤休,程序占用了過高的內(nèi)存就容易出現(xiàn)OOM(OutOfMemory)異常。我們可以通過下面的代碼看出每個(gè)應(yīng)用程序最高可用內(nèi)存是多少症副。
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
Log.d("TAG", "Max memory is " + maxMemory + "KB");
因此在展示高分辨率圖片的時(shí)候店雅,最好先將圖片進(jìn)行壓縮政基。壓縮后的圖片大小應(yīng)該和用來展示它的控件大小相近,在一個(gè)很小的ImageView上顯示一張超大的圖片不會(huì)帶來任何視覺上的好處闹啦,但卻會(huì)占用我們相當(dāng)多寶貴的內(nèi)存腋么,而且在性能上還可能會(huì)帶來負(fù)面影響。下面我們就來看一看亥揖,如何對(duì)一張大圖片進(jìn)行適當(dāng)?shù)膲嚎s珊擂。
BitmapFactory
這個(gè)類提供了多個(gè)解析方法(decodeByteArray, decodeFile, decodeResource等)用于創(chuàng)建Bitmap對(duì)象,我們應(yīng)該根據(jù)圖片的來源選擇合適的方法费变。比如SD卡中的圖片可以使用decodeFile方法摧扇,網(wǎng)絡(luò)上的圖片可以使用decodeStream方法,資源文件中的圖片可以使用decodeResource方法挚歧。這些方法會(huì)嘗試為已經(jīng)構(gòu)建的bitmap分配內(nèi)存扛稽,這時(shí)就會(huì)很容易導(dǎo)致OOM出現(xiàn)。為此每一種解析方法都提供了一個(gè)可選的BitmapFactory.Options參數(shù)滑负,將這個(gè)參數(shù)的inJustDecodeBounds屬性設(shè)置為true就可以讓解析方法禁止為bitmap分配內(nèi)存在张,返回值也不再是一個(gè)Bitmap對(duì)象,而是null矮慕。雖然Bitmap是null了帮匾,但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會(huì)被賦值痴鳄。這個(gè)技巧讓我們可以在加載圖片之前就獲取到圖片的長(zhǎng)寬值和MIME類型瘟斜,從而根據(jù)情況對(duì)圖片進(jìn)行壓縮。如下代碼所示:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
現(xiàn)在圖片的大小已經(jīng)知道了痪寻,我們就可以決定是把整張圖片加載到內(nèi)存中還是加載一個(gè)壓縮版的圖片到內(nèi)存中螺句。以下幾個(gè)因素是我們需要考慮的:
- 預(yù)估一下加載整張圖片所需占用的內(nèi)存。
- 為了加載這一張圖片你所愿意提供多少內(nèi)存橡类。
- 用于展示這張圖片的控件的實(shí)際大小蛇尚。
- 當(dāng)前設(shè)備的屏幕尺寸和分辨率。
比如顾画,你的ImageView只有12896像素的大小取劫,只是為了顯示一張縮略圖,這時(shí)候把一張1024768像素的圖片完全加載到內(nèi)存中顯然是不值得的亲雪。
那我們?cè)鯓硬拍軐?duì)圖片進(jìn)行壓縮呢勇凭?通過設(shè)置BitmapFactory.Options中inSampleSize的值就可以實(shí)現(xiàn)。比如我們有一張20481536像素的圖片义辕,將inSampleSize的值設(shè)置為4虾标,就可以把這張圖片壓縮成512384像素。原本加載這張圖片需要占用13M的內(nèi)存灌砖,壓縮后就只需要占用0.75M了(假設(shè)圖片是ARGB_8888類型璧函,即每個(gè)像素點(diǎn)占用4個(gè)字節(jié))傀蚌。下面的方法可以根據(jù)傳入的寬和高,計(jì)算出合適的inSampleSize值:
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源圖片的高度和寬度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 計(jì)算出實(shí)際寬高和目標(biāo)寬高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 選擇寬和高中最小的比率作為inSampleSize的值蘸吓,這樣可以保證最終圖片的寬和高
// 一定都會(huì)大于等于目標(biāo)的寬和高善炫。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
萬事俱備只欠封裝了,我們現(xiàn)在知道了如何獲取圖片的大小和對(duì)圖片進(jìn)行壓縮库继,下一步我們就將兩者結(jié)合箩艺,定義一個(gè)公共方法供我們以后使用
public static int showPhotoInformation(String path,int reqWidth,int reqHeight) {
//獲取圖片的大小和類型
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path,options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
//根據(jù)期望的寬和高(單位是dp)計(jì)算壓縮的inSampleSize
int inSampleSize = 1;
if (imageHeight > reqHeight || imageWidth > reqWidth) {
// 計(jì)算出實(shí)際寬高和目標(biāo)寬高的比率
final int heightRatio = Math.round((float) imageHeight / (float) reqHeight);
final int widthRatio = Math.round((float) imageWidth / (float) reqWidth);
// 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高
// 一定都會(huì)大于等于目標(biāo)的寬和高宪萄。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
Log.e("imageInformation","imageHeight="+imageHeight+"imageWidth="+imageWidth+"imageType="+imageType);
//返回圖片壓縮的比例
return inSampleSize;
}
下面還有一段代碼是用來加載圖片的
public static Bitmap returnBitmap(String path,int size){
//path代表圖片的真實(shí)路徑和size代表圖片壓縮的比例
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = size;
return BitmapFactory.decodeFile(path,options);
}
具體的調(diào)用如下,CommonUtil是自定義的公共類
String imagePath = handleImageOnKitKat(intent);
int size = CommonUtil.showPhotoInformation(imagePath,50,50); header_im1.setImageBitmap(CommonUtil.returnBitmap(imagePath,size));
注意
上一篇博客加載頭像圖片時(shí)也是采用bitmap的方法艺谆,細(xì)心的同學(xué)可能會(huì)發(fā)現(xiàn)相冊(cè)里面都是些大圖片,直接加載會(huì)不會(huì)出現(xiàn)OOM呢拜英,答案是可能的静汤,所以當(dāng)選擇完圖片后,選擇的圖片顯示全黑或者程序崩潰居凶,我們就應(yīng)該考慮壓縮圖片再顯示了
以上就是如何高效加載大圖的所有代碼了虫给,如有幫助請(qǐng)點(diǎn)個(gè)贊吧!O辣獭抹估!
如有問題,歡迎留言討論S叽病F灏觥!