Android中高效的顯示圖片 - 加載大圖

memory_leak.png

java.lang.OutofMemoryError: bitmap size exceeds VM budget

Android開發(fā)者應(yīng)該對上面這個(gè)錯(cuò)誤都不陌生陪捷。Android系統(tǒng)對每個(gè)應(yīng)用使用的內(nèi)存是有限制的厂庇,一旦應(yīng)用使用的總內(nèi)存超過這個(gè)閥值善绎,系統(tǒng)就會(huì)拋出上面的錯(cuò)誤導(dǎo)致應(yīng)用crash。內(nèi)存溢出的錯(cuò)誤是開發(fā)者必須要解決的永淌,根據(jù)經(jīng)驗(yàn)來說蔫耽,內(nèi)存溢出很多場景都是由圖片資源使用不當(dāng)引起的。Android官方的開發(fā)者文檔中也有專門的文章來介紹這個(gè)問題抑党。這個(gè)系列的文章是閱讀官方文檔后的一個(gè)筆記。

本文出處:http://www.reibang.com/p/590f61222637

高效的加載高分辨率的圖片

我們以加載Galaxy Nexus拍攝的照片為例撵摆。500萬的攝像頭拍攝的照片分辨率為2592x1936像素底靠,如果我們使用ARGB_8888設(shè)置(android 2.3以后的默認(rèn)設(shè)置,這種設(shè)置規(guī)定用四個(gè)字節(jié)來存儲(chǔ)一個(gè)像素值)來加載這張圖片特铝,它將占用19M的內(nèi)存(2592*1936*4字節(jié))苛骨。在某些限制每個(gè)應(yīng)用最多使用16M內(nèi)存的手機(jī)上,這一張圖片就會(huì)導(dǎo)致內(nèi)存溢出苟呐。

出現(xiàn)這種情況怎么辦呢痒芝?冷靜分析我們會(huì)發(fā)現(xiàn)在手機(jī)上展示圖片時(shí)我們并不需要這么高分辨率的圖片。比如在屏幕分辨率為1920x1080的手機(jī)上牵素,即使你要展示圖片的imageview充滿了整個(gè)屏幕严衬,也最多需要1920x1080的圖片,更高分辨率的圖片對我們的展示效果并沒有提升笆呆,只會(huì)白白浪費(fèi)我們寶貴的內(nèi)存空間请琳。所以對這種加載高分辨率的圖片情況我們應(yīng)該加載一個(gè)低分辨率版本的圖片到內(nèi)存中。接下來我們看下具體的操作步驟赠幕。

  1. 加載圖片尺寸和類型
    針對不同的圖片數(shù)據(jù)來源俄精,BitmapFactory提供了不同的解碼方法(decodeResource()、decodeFile()...)榕堰,這些方法在構(gòu)造圖片的時(shí)候會(huì)申請相應(yīng)的內(nèi)存空間竖慧,所以它們經(jīng)常拋出內(nèi)存溢出的異常。這些方法都允許傳入一個(gè)BitmapFactory.Options類型的參數(shù)來獲取將要構(gòu)建的圖片的屬性逆屡。如果將inJustDecodeBounds的值設(shè)置成true圾旨,這些方法將不會(huì)真正的創(chuàng)建圖片,也就不會(huì)占用內(nèi)存魏蔗,它們的返回值將會(huì)是空砍的。但是它們會(huì)讀取圖片資源的屬性,我們就可以從BitmapFactory.Options中獲取到圖片的尺寸和類型莺治。
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;

解碼圖片之前先檢查圖片的大小可以有效的避免內(nèi)存溢出廓鞠,除非你很確定你要加載的圖片不會(huì)導(dǎo)致內(nèi)存溢出。

  1. 加載縮小比例的圖片到內(nèi)存中
    現(xiàn)在圖片的尺寸已經(jīng)獲取了谣旁,接下來就是判斷是將圖片全尺寸加載到內(nèi)存還是加載一個(gè)縮小版床佳。判斷的標(biāo)準(zhǔn)有以下幾條:
    • 全尺寸的圖片需要占用多大內(nèi)存空間;
    • 應(yīng)用能夠提供給這張圖片的內(nèi)存空間的大新凇夕土;
    • 展示這張圖片的imageview或其他UI組件的大小;
    • 設(shè)備的屏幕大小和密度怨绣。

例如角溃,填充100x100縮略圖imageview時(shí)就沒有必要將1024x768的圖片全尺寸的加載到內(nèi)存中。這時(shí)就要設(shè)置BitmapFactory.Options中的inSampleSize屬性來告訴解碼器來加載一個(gè)縮小比例的圖片到內(nèi)存中篮撑。如果以inSampleSize=4的設(shè)置來解碼這張1024x768的圖片减细,將會(huì)得到一張256x196的圖片,加載這張圖片使用的內(nèi)存會(huì)從3M減少到196K 赢笨。inSampleSize的值是根據(jù)縮放比例計(jì)算出來的一個(gè)2的n次冪的數(shù)未蝌。下面是計(jì)算inSampleSize常用的方法。

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {  
    // Raw height and width of image  
    final int height = options.outHeight;  
    final int width = options.outWidth;  
    int inSampleSize = 1;  

    if (height > reqHeight || width > reqWidth) {    
        final int halfHeight = height / 2;    
        final int halfWidth = width / 2;    
        // Calculate the largest inSampleSize value that is a power of 2 and keeps both    
        // height and width larger than the requested height and width.    
        while ((halfHeight / inSampleSize) >= reqHeight&& (halfWidth / inSampleSize) >= reqWidth) {      
            inSampleSize *= 2;    
        }  
    }  

    return inSampleSize;
}

加載縮小比例圖片大致有三個(gè)步驟:inJustDecodeBounds設(shè)置為true獲取原始圖片尺寸 --> 根據(jù)使用場景計(jì)算縮放比例inSampleSize --> inJustDecodeBounds設(shè)置為false茧妒,傳遞inSampleSize給解碼器創(chuàng)建縮小比例的圖片萧吠。示例代碼如下:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,int reqWidth, int reqHeight) {  
    // First decode with inJustDecodeBounds=true to check dimensions  
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;  
    BitmapFactory.decodeResource(res, resId, options);  

    // Calculate inSampleSize  
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);  

    // Decode bitmap with inSampleSize set  
    options.inJustDecodeBounds = false;  
    return BitmapFactory.decodeResource(res, resId, options);
}

有了上面的方法我們就可以很輕松的為大小為100x100縮略圖imageview加載圖片了。

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

本文是《Android中高效的顯示圖片》專題中的第一篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桐筏,一起剝皮案震驚了整個(gè)濱河市纸型,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梅忌,老刑警劉巖狰腌,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牧氮,居然都是意外死亡琼腔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門踱葛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丹莲,“玉大人,你說我怎么就攤上這事剖毯』浚” “怎么了教馆?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵逊谋,是天一觀的道長。 經(jīng)常有香客問我土铺,道長胶滋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任悲敷,我火速辦了婚禮究恤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘后德。我一直安慰自己部宿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著理张,像睡著了一般赫蛇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雾叭,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天悟耘,我揣著相機(jī)與錄音,去河邊找鬼织狐。 笑死暂幼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的移迫。 我是一名探鬼主播旺嬉,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼厨埋!你這毒婦竟也來了鹰服?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤揽咕,失蹤者是張志新(化名)和其女友劉穎悲酷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亲善,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡设易,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蛹头。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顿肺。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖渣蜗,靈堂內(nèi)的尸體忽然破棺而出屠尊,到底是詐尸還是另有隱情,我是刑警寧澤耕拷,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布讼昆,位于F島的核電站,受9級特大地震影響骚烧,放射性物質(zhì)發(fā)生泄漏浸赫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一赃绊、第九天 我趴在偏房一處隱蔽的房頂上張望既峡。 院中可真熱鬧,春花似錦碧查、人聲如沸运敢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽传惠。三九已至肤视,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涉枫,已是汗流浹背邢滑。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愿汰,地道東北人困后。 一個(gè)月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像衬廷,于是被迫代替她去往敵國和親摇予。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

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