【原創】加載一張圖片到ImageView到底占據多少內存


0.內容概覽

  • 1. 簡介
  • 2. 問題
  • 3. 概念描述
  • 4. 具體分析
  • 5. 總結
  • 6. 參考文檔

1.簡介

      Android中經常要通過ImageView進行圖片資源顯示。在加載圖片時,首先要考慮的兩個因素就是體驗問題和性能問題。

      其中,體驗問題是指圖片顯示的是否正確(例如Universal-Image-Loader在適配Adapter圖片資源時會導致圖片顯示錯位),分辨率是否合適等。而體驗問題主要是指圖片加載速度,以及更加重要的圖片加載的內存占用問題。本文重點介紹ImageView加載圖片中的內存占用問題。

2.問題

      在開發的過程中,經常會發現因為加載圖片而引起的Out of Memory(OOM)問題,有的時候會很奇怪,明明加載的圖片只有幾十K,為什么就會導致OOM呢?另外在圖片資源設置過程中,只放置某一個dpi文件夾(例如drawable-xxhdpi文件夾)的資源,在不同設備中對ImageView占用會有影響嗎?下面會對圖片加載的內存占用相關問題進行展開分析。

3.概念描述

      首先,圖片對內存的占用是一個疊加的過程,也就是說圖片資源不是及時釋放的,使用過的圖片在回收過程中可能會有一定程度的延遲。此外,很多時候圖片所依附的Activity是出於當前Activity棧底的狀態,再GC回收過程,這樣的bitmap資源會被認為是活躍狀態的,不會被Android系統回收。

      另外一方面,Android中圖片加載到內存中的內存占用跟圖片的實際大小沒有直接的關系,甚至於圖片的實際像素尺寸也沒有直接的關系。

      在這里,首先要介紹幾個概念(以圖片A:尺寸60*60 大小2.02K為例):

  • 圖像尺寸:表示圖像在硬盤中的原始尺寸,本例中為60*60
  • 圖像大小:表示圖像在占據硬盤容量大小,本例中為2.02K
  • bitmap尺寸:表示圖像以bitmap的形式存在內存中的實際尺寸;
  • 顯示尺寸:表示圖像在UI上顯示的實際尺寸;
  • 內存占用:表示加載的圖片以bitmap的形式在內存中的實際占用。

     其中,bitmap尺寸和內存占用可以通過下面的方法得到:

 1     /***  2  * 計算ImageView中加載圖片的具體尺寸和內存占用大小  3  * @param imageView  4      */
 5     private void calculateBitmapInfo(ImageView imageView) {  6         Drawable drawable = imageView.getDrawable();  7         if (drawable != null) {  8             BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;  9             Bitmap bitmap = bitmapDrawable.getBitmap(); 10             Log.d(TAG, " bitmap width = " + bitmap.getWidth() + " bitmap height = " + bitmap.getHeight()); 11             Log.d(TAG, " memory usage = " + bitmap.getAllocationByteCount());/**bitmap.getByteCount()方法不再使用*/
12         } else { 13             Log.d(TAG, "drawable is null!"); 14  } 15     }

4.具體分析

      為了明確這個問題,我們做了以下的幾組實驗對比,具體如下:

4.1實驗1

測試方案:將尺寸為圖片A(尺寸60*60 大小2.02K),圖片B(尺寸60*60 大小1.63K),將圖片均放入drawable-xxhdpi文件夾,圖片顯示尺寸采用wrap_content,用華為mate 9(xxhdpi)手機進行測試;

測試結果:二者內存占用均為14400Byte,bitmap尺寸為60*60;

結果分析:說明內存占用單獨與圖片原始大小沒有關系。

4.2實驗2

測試方案:將將尺寸為圖片A(尺寸60*60 大小2.02K)放入drawable-xxhdpi文件夾,圖片顯示尺寸設置為30dp*30dp和60dp*60dp,分別用mate 9手機進行測試;

測試結果:內存占用均為14400Byte,bitmap尺寸均為60*60;

結果分析:說明內存占用與圖片的實際顯示尺寸沒有關系。

4.3實驗3

測試方案3:將將尺寸為圖片A(尺寸60*60 大小2.02K)放入drawable和drawable-xxhdpi文件夾,圖片顯示尺寸采用wrap_content,分別用mate 9手機進行測試;

測試結果:內存占用分別為129600Byte和14400Byte,圖片在ImageView中的bitmap尺寸為180*180和60*60;

結果分析:說明內存占用與圖片的原始尺寸沒有關系,與bitmap尺寸有密切的關系。

5.總結

      經過上面的測試,可以發現:

      內存占用 = bitmap width * bitmap height * 顏色深度(單位Byte)

      很多文章總結內存占用 = (原始圖像尺寸)width * height * 顏色深度(單位Byte),是有一定的問題的。

      顏色深度表征圖片顯示中每個像素的內存占用,常用的顏色深度有以下4類:

(1).Bitmap.Config ALPHA_8

      8位Alpha位圖(沒有RGB通道值),每個像素占據1Byte內存

(2).Bitmap.Config ARGB_4444

      16位Alpha位圖(ARGB每個通道各占4位),每個像素占據2Byte內存

(3).Bitmap.Config ARGB_8888

      默認顏色深度,32位Alpha位圖(ARGB每個通道各占8位),每個像素占據4Byte內存

(4).Bitmap.Config RGB_565

      16位Alpha位圖(RGB每個通道分別占據5,6,5位,沒有Alpha通道),每個像素占據2Byte內存。此顏色深度下,圖片不支持透明度效果,但是也因此相對ARGB_8888內存占據較小,所以廣泛適用於不設定透明度的圖片顯示上。查閱ImageView源碼,發現ImageView中圖片是以Bitmap形式保存在內存中;查閱Bitmap源碼,發現圖像在內存中的實際bitmap尺寸和圖像的原始尺寸(withPixel * heightPixel),資源文件像素密度(sourcedensity)以及目標手機的像素密度(targetDensity)有密不可分的關系,具體如下:

    上面的源碼是以高度為例的,寬度可以類比高度的實現。具體換算公式可以表示如下:

  • bitmap width = (withPixel*targetDensity +sourceDensity>>1)/sourceDensity
  • bitmap height = (heightPixel*targetDensity +sourceDensity>>1)/sourceDensity

     其中sourceDensity>>1部分是為了對targetDensity/sourceDensity進行四舍五入。通過實驗1,2,3的數據進行驗證,可以發現公式的正確性。

     另外,在多dpi適配過程中,一般要在不同的dpi資源文件中放置不同尺寸的圖片,也就是說,withPixel、heightPixel和sourceDensity將等比例變化,所以整體的內存占用其實和資源文件在哪個dpi文件夾是無關的。也就是說,只放置單個dpi文件夾的資源,對不同設備加載同一個資源的內存占用沒有影響。

     綜上,ImageView中顯示圖像對內存的占用與原始圖像尺寸,資源文件的dpi,以及實際設備的dpi有密切的關系,與圖像在UI上實際顯示的尺寸無關。只放置單dpi資源文件,對不同設備加載過程中的內存占用無影響。

6.參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM