2018-05-28

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灏觥!

以上代碼參考了 郭霖的博客 https://blog.csdn.net/guolin_blog/article/details/9316683

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挨队,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒿往,更是在濱河造成了極大的恐慌盛垦,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓤漏,死亡現(xiàn)場(chǎng)離奇詭異腾夯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蔬充,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門蝶俱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饥漫,你說我怎么就攤上這事榨呆。” “怎么了庸队?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵积蜻,是天一觀的道長(zhǎng)闯割。 經(jīng)常有香客問我,道長(zhǎng)竿拆,這世上最難降的妖魔是什么宙拉? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮丙笋,結(jié)果婚禮上谢澈,老公的妹妹穿的比我還像新娘。我一直安慰自己御板,他們只是感情好澳化,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稳吮,像睡著了一般缎谷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灶似,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天列林,我揣著相機(jī)與錄音,去河邊找鬼酪惭。 笑死希痴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的春感。 我是一名探鬼主播砌创,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鲫懒!你這毒婦竟也來了嫩实?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤窥岩,失蹤者是張志新(化名)和其女友劉穎甲献,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颂翼,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晃洒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朦乏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片球及。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖呻疹,靈堂內(nèi)的尸體忽然破棺而出吃引,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布际歼,位于F島的核電站惶翻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鹅心。R本人自食惡果不足惜吕粗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旭愧。 院中可真熱鬧颅筋,春花似錦、人聲如沸输枯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桃熄。三九已至先口,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瞳收,已是汗流浹背碉京。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留螟深,地道東北人谐宙。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像界弧,于是被迫代替她去往敵國(guó)和親凡蜻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 2021期待與你一起共事垢箕,點(diǎn)擊查看崗位[http://www.reibang.com/p/6f4d67fa406...
    閑庭閱讀 16,625評(píng)論 0 75
  • 一直以來Bitmap都是開發(fā)中很棘手的問題划栓,這個(gè)問題就是傳說中的OOM(java.lang.OutofMemory...
    M悇芐冋憶閱讀 4,734評(píng)論 0 11
  • 7.1 壓縮圖片 一、基礎(chǔ)知識(shí) 1舰讹、圖片的格式 jpg:最常見的圖片格式茅姜。色彩還原度比較好,可以支持適當(dāng)壓縮后保持...
    AndroidMaster閱讀 2,497評(píng)論 0 13
  • 摘要:對(duì)android 上圖片壓縮月匣,其實(shí)總結(jié)起來基本可以分為兩類壓縮:尺寸壓縮和質(zhì)量壓縮, 尺寸壓縮其實(shí)也可以理解...
    男爵是只貓丶閱讀 8,772評(píng)論 2 14
  • 雖然已經(jīng)結(jié)營(yíng)了奋姿,但是還是得把總結(jié)發(fā)上來锄开。畢竟堅(jiān)持輸出更重要。 古人云“師傅領(lǐng)進(jìn)門称诗,修行靠個(gè)人”萍悴。古往今來,也一直提...
    迷途的時(shí)一閱讀 615評(píng)論 0 1