Bitmap避免OOM

目錄介紹

  • 01.先看一個需求分析案例
  • 02.Bitmap佔用內存介紹
  • 03.影響Bitmap佔用內存因素
  • 04.圖像加載的方式
  • 05.加載圖像內存去哪裏了
  • 06.具體實現加載圖片步驟

但願世麟兄弟母親儘快好起來

  • 和南塵(世麟)認識與網絡,估計好多程序員都看過他的文章,我也是。雖然沒有見過面,可是微信聊過屢次,感受人很是不錯。都具備共同的愛好,喜歡寫技術博客和開源項目,算得上同道中人。
  • 對於此次他遇到的事情,個人確很佩服他那種承擔的責任和勇氣。對於任何一個出身通常的人來講,大多數家庭都是難以承擔疾病所帶來的費用,即便有勇氣承擔,那經濟上也的確讓人壓力很大,若是能夠盡綿薄之力,那就特別感謝你們呢!
  • 雖然大多數技術平臺發這個水滴籌,有些人會表示不理解,有的甚至說會影響社區氛圍。我以爲這種擔憂很正常,可是有點把問題放大了,首先這個是一個特別小几率的事件,並不會存在說你們都這樣作就形成很差的影響。其次有人還說,會過渡消費社會這種同情和愛心,這種擔憂挺好,可是人老是會有分辨是非的能力,若是可以幫忙那就盡綿薄之力,若是不能幫忙那也不要亂扣帽子。
  • 我知道最近世麟心情是錯綜複雜,但還好的是絕大多數程序員都是特別有善心的。我始終以爲一個可以堅持寫技術博客,並且還寫了這麼多,掘金仍是之前的掘金,沒有發生變化。
  • 程序員爸爸癱瘓14年,媽媽又這樣,幫幫南塵!

01.先看一個需求分析案例

  • 案例說明php

    • 加載一個本地的大圖片或者網絡圖片,從加載到設置到View上,如何減下內存,避免加載圖片OOM。
  • 案例分析android

    • 在展現高分辨率圖片的時候,最好先將圖片進行壓縮。壓縮後的圖片大小應該和用來展現它的控件大小相近,在一個很小的ImageView上顯示一張超大的圖片不會帶來任何視覺上的好處,但卻會佔用至關多寶貴的內存,並且在性能上還可能會帶來負面影響。

02.Bitmap佔用內存介紹

  • 網絡圖片計算Bitmap的內存大小git

    • bitmap內存大小 = 圖片長度 x 圖片寬度 x 單位像素佔用的字節數
    • 起決定因素就是最後那個參數了,Bitmap'常見有2種編碼方式:ARGB_8888和RGB_565,ARGB_8888每一個像素點4個byte,RGB_565是2個byte,通常都採用ARGB_8888這種。那麼常見的1080*1920的圖片內存佔用就是:1920 x 1080 x 4 = 7.9M
  • 加載本地資源計算Bitmap的內存大小程序員

    • 加載一張本地資源圖片,那麼它佔用的內存 = width height nTargetDensity/inDensity 一個像素所佔的內存。
    • 詳細能夠看這篇文章04.Bitmap計算內存
  • 正確說法,這個注意呢?計算公式以下所示github

    • 對資源文件:width height nTargetDensity/inDensity nTargetDensity/inDensity 一個像素所佔的內存;
    • 別的:width height 一個像素所佔的內存;

03.影響Bitmap佔用內存因素

  • 影響Bitmap佔用內存的因素:segmentfault

    • 圖片最終加載的分辨率;
    • 圖片的格式(PNG/JPEG/BMP/WebP);
    • 圖片所存放的drawable目錄;
    • 圖片屬性設置的色彩模式;
    • 設備的屏幕密度;

04.圖像加載的方式

  • 獲取圖像的來源通常有三種源頭:微信

    • 1.從網絡加載2.從文件讀取3.從資源文件加載
  • 針對這三種狀況咱們通常使用BitmapFactory的網絡

    • decodeStream,decodeFile,decodeResource,這三個函數來獲取到bitmap而後再調用ImageView的setImageBitmap函數進行展示。

05.加載圖像內存去哪裏了

  • 思考一下:內存去哪裏了(爲何被消耗了這麼多)?ide

    • 其實咱們的內存就是去bitmap裏了,BitmapFactory的每一個decode函數都會生成一個bitmap對象,用於存放解碼後的圖像,而後返回該引用。若是圖像數據較大就會形成bitmap對象申請的內存較多,若是圖像過多就會形成內存不夠用天然就會出現out of memory的現象。
  • 爲什麼容易OOM?函數

    • 經過BitmapFactory的decode的這些方法會嘗試爲已經構建的bitmap分配內存,這時就會很容易致使OOM出現。爲此每一種解析方法都提供了一個可選的BitmapFactory.Options參數,將這個參數的inJustDecodeBounds屬性設置爲true就可讓解析方法禁止爲bitmap分配內存,返回值也再也不是一個Bitmap對象,而是null。

06.具體實現加載圖片步驟

  • 爲了不OOM異常,最好在解析每張圖片的時候都先檢查一下圖片的大小,除非你很是信任圖片的來源,保證這些圖片都不會超出你程序的可用內存。
  • 如今圖片的大小已經知道了,咱們就能夠決定是把整張圖片加載到內存中仍是加載一個壓縮版的圖片到內存中。如下幾個因素是咱們須要考慮的:

    • 預估一下加載整張圖片所需佔用的內存。
    • 爲了加載這一張圖片你所願意提供多少內存。
    • 用於展現這張圖片的控件的實際大小。
    • 當前設備的屏幕尺寸和分辨率。
  • 好比,你的ImageView只有128x96像素的大小,只是爲了顯示一張縮略圖,這時候把一張2048x1536像素的圖片徹底加載到內存中顯然是不值得的。

6.1 對圖片進行壓縮

  • 怎樣才能對圖片進行壓縮呢?

    • 經過設置BitmapFactory.Options中inSampleSize的值就能夠實現。
  • 好比咱們有一張2048x1536像素的圖片,將inSampleSize的值設置爲4,就能夠把這張圖片壓縮成512x384像素。

    • 本來加載這張圖片須要佔用13M的內存,壓縮後就只須要佔用0.75M了(假設圖片是ARGB_8888類型,即每一個像素點佔用4個字節)。
  • 下面的方法能夠根據傳入的寬和高,計算出合適的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) { 
            // 計算出實際寬高和目標寬高的比率 
            final int heightRatio = Math.round((float) height / (float) reqHeight); 
            final int widthRatio = Math.round((float) width / (float) reqWidth); 
            // 選擇寬和高中最小的比率做爲inSampleSize的值,這樣能夠保證最終圖片的寬和高 
            // 必定都會大於等於目標的寬和高。 
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 
        } 
        return inSampleSize; 
    }

6.2 設置BitmapFactory.Options屬性

  • 大概步驟以下所示

    • 要將BitmapFactory.Options的inJustDecodeBounds屬性設置爲true,解析一次圖片。注意這個地方是核心,這個解析圖片並無生成bitmap對象(也就是說沒有爲它分配內存控件),而僅僅是拿到它的寬高等屬性。
    • 而後將BitmapFactory.Options連同指望的寬度和高度一塊兒傳遞到到calculateInSampleSize方法中,就能夠獲得合適的inSampleSize值了。這一步會壓縮圖片。
    • 以後再解析一次圖片,使用新獲取到的inSampleSize值,並把inJustDecodeBounds設置爲false,就能夠獲得壓縮後的圖片了。此時才正式建立了bitmap對象,因爲前面已經對它壓縮了,因此你會發現此時所佔內存大小已經不多了。
  • 具體的實現代碼

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, 
            int reqWidth, int reqHeight) { 
        // 第一次解析將inJustDecodeBounds設置爲true,來獲取圖片大小 
        final BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inJustDecodeBounds = true; 
        BitmapFactory.decodeResource(res, resId, options); 
        // 調用上面定義的方法計算inSampleSize值 
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 
        // 使用獲取到的inSampleSize值再次解析圖片 
        options.inJustDecodeBounds = false; 
        return BitmapFactory.decodeResource(res, resId, options); 
    }
  • 思考:inJustDecodeBounds這個參數是幹什麼的?

    • 若是設置爲true則表示decode函數不會生成bitmap對象,僅是將圖像相關的參數填充到option對象裏,這樣咱們就能夠在不生成bitmap而獲取到圖像的相關參數了。
  • 爲什麼設置兩次inJustDecodeBounds屬性?

    • 第一次:設置爲true則表示decode函數不會生成bitmap對象,僅是將圖像相關的參數填充到option對象裏,這樣咱們就能夠在不生成bitmap而獲取到圖像的相關參數。
    • 第二次:將inJustDecodeBounds設置爲false再次調用decode函數時就能生成bitmap了。而此時的bitmap已經壓縮減少不少了,因此加載到內存中並不會致使OOM。

6.3 設置bitmap到View上

  • 將任意一張圖片壓縮成100*100的縮略圖,並在ImageView上展現。

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

其餘介紹

01.關於博客彙總連接

02.關於個人博客

項目案例:https://github.com/yangchong2...

相關文章
相關標籤/搜索