怎樣計算Bitmap的內存佔用和Bitmap加載優化

介紹

在Android開發中Bitmap確定是繞不過去的,不少時候咱們只是使用圖片框架加載圖片,具體細節的Bitmap對內存的使用圖片框架已經幫咱們封裝好了。可是對Btimap對內存的影響咱們仍是須要了解的。html

內存佔用

首先要清楚Bitmap的文件大小確定不是實際的內存加載大小。由於文件只是存儲的信息,加載到內存中顯示出來時還須要通過轉換。android

獲取運行的時的內存佔用: 針對Bitmap位圖對象,Android的系統框架中的graphics包下的BItmap類。有bitmap.getByteCount()方法獲取內存大小,單位字節(byte) 其實本質上Bitmap的內存佔用計算很是簡單:網絡

基本公式:總內存=寬×高×色彩空間框架

可是在實際運行中不是這麼簡單,公式的每一個參數都會有被不一樣因素影響。ide

影響內存大小的三要素

 咱們這裏不從代碼出發,直接說重點不會雲山霧罩的。 之前文的公式爲基礎。 影響內存大小的三要素優化

  1. 圖片寬高。
  2. 色彩空間
  3. 縮放比 下面一個個說明

圖片寬高

咱們在AS打開圖片看到的文件信息 .net

文件信息
其中的 1000×447就是圖片寬高,或者說是原始寬高。

做用:寬高的乘積描述圖片的像素點總數,也就是這個圖片由多少像素構成。code

它通常不是咱們最終加載到內存時的圖片寬高,可是能夠認爲是基礎變量。orm

色彩空間

即Bitmap.Config枚舉:cdn

做用:色彩空間描述每一個像素點的信息

ARGB_8888:

總共32位(4byte),分別對應4個數值,數值單位爲8bit位=1byte字節,分別描述透明度(1個)+RGB通道(3個)。每一個字節數值範圍0-255。做爲Bitmap配置色彩空間的默認值。BitmapFactory加載時默認。 public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888

RGB_565:

總共16位(2byte),分別對應3個數值,5位(紅)+6位(綠)+5位(藍)分別描述RGB通道。Glide加載時默認使用,DecodeFormat類 public static final DecodeFormat DEFAULT = PREFER_RGB_565

能夠看到,RGB_565只須要ARGB_8888的一半大小,代價是沒有透明度描述。

縮放比

做用:對圖片原始寬高的縮放設置認定爲縮放比。

首先須要指出,縮放比在內存的影響上是次方的,影響值=縮放比×縮放比。這是由於縮放比分別做用在寬高上,使用了兩次。 縮放比會被不少因素影響。

主動設置:

在解析Bitmap時,有個可選的Options對象,其中inSampleSize參數能夠影響縮放比的結果。當使用該參數值時要求大於1且是2的倍數,好比在inSampleSize=2時,縮放比被縮小2倍(該功能只有縮小沒有放大的可能),即「縮放比=原始縮放比×(1/2)」。對內存結果的影響是縮小4倍,由於寬/高都被縮小2倍。該值默認不生效,須要手動設置。

被動設置

在系統中主要由,具體運行設備dpi的和圖片文件存儲的drawable文件dpi層級決定。 首先要引出屏幕密度概念。這是Android爲應對衆多的不一樣的屏幕分辨率色設備提出的概念和單位。

  • drawable文件的dpi層級:在Drawable系列文件中保存的Bitmap位圖文件。根據Android開發的規範,Drawable的系列文件中的修飾符後命名是有意義的,聲明瞭這個文件所屬的Dpi(屏幕密度)層級。文件衆多也對應了衆多的Android設備屏幕密度。
  • 設備的dpi層級:參考https://material.io/devices/能夠了解。

設備和drawable文件的dpi縮放比計算: 好比drawable-xhdpi(320=2160=2mdpi)下的bitmap被加載到xxxhdpi(640=4160=4mdpi)的Pixel-XL中。 dpi縮放比=設備dpi/drawable的dpi,因此上面的,dpi縮放比=640/320=2。 實際意義就是在高分辨率的xxxhdpi設備中,drawable-xhdpi文件須要放大2倍,即寬高各放大2倍來適應高密度設備。須要指出的是若是Bitmap文件保存在drawable沒有後綴的文件中,系統會使用drawable的dpi默認層級就是160;

須要說明的是:這個縮放比只在當Bitmap文件位於drawable這樣有dpi層級的文件中時生效。若是Bitmap位圖文件位於assets包這樣的外部文件或者是URL網絡地址,是有沒有縮放比的(即默認爲1)不影響內存結果。這個也好理解,當讀取文件時得不到其餘的信息就不須要再處理了。有趣的是Glide也是存在縮放比概念的,可是和dpi無關,和View視圖有關。

Glide縮放比=View視圖值/原圖值 ,這裏的值是不固定的,由於縮放比只有一個,可是視圖寬高和原圖寬高的比值有兩個,須要權衡取出可以同時應用於寬高的縮放比。

最終縮放比

最後的縮放比結果:

縮放比=主動設置縮放比*被動設置縮放比

主動縮放比:沒有設置時,默認爲1,即不影響另外的值。 被動縮放比:這須要看能不能獲得設備和drawable的關係(或者其餘關係,如Glide的視圖圖片比例)若是不能獲得也是1。

最終公式

基於以上認識,豐富上文的基本公式,能夠的獲得最終的計算Bitmap內存公式

最終公式:總內存=(原始寬×縮放比)×(原始高×縮放比)×色彩空間

驗證

原圖:1000寬X447高,位於drawable-xxhdpi(480dpi=3160dpi)文件包,設備Pixel-XL(560dpi=3.5160dpi)。主動設置inSampleSize=2。使用默認Bitmap.Config=ARGB_8888

  • 縮放比=主動設置×被動設置(dpi層級關係)=1/2×(560/480)=0.5×1.166=0.5833
  • 色彩空間=ARGB_8888=32bit=4byte
  • 原始大小=1000×447

內存佔用=(原始寬×縮放比)×(原始高×縮放比)×色彩空間 =1000×0.5833×447×0.5833×4 =583×260×4 =606320byte ≈0.578MB

最後上圖驗證:

最終結果

計算結果完美匹配運行結果,這裏說明爲何故意省略小數點3位之後,這是對應native層代碼的float小數點結果轉int時的捨棄。

啓示

理解Bitmap的最終內存佔用計算原理和內存佔用各個參數,咱們對Bitmap的處理時就有具體的目標。好比常見的優化Bitmap加載過程,其實就是對Bitmap加載時的各個變量參數設置修改。 常見的Bitmap優化:

  • 修改主動縮放比:目標是修改最終圖片加載的寬高,進而優化內存佔用。具體就是設置inSampleSize值,如在適當的View上縮放顯示適合的bitmap,實現bitmap的高效加載(Glide圖片框架就是這樣,讓顯示組件View的寬高的參與縮放比計算)。
  • 修改被動縮放比:理解了被動縮放比的計算,設備dpi層級和圖片dpi層級關係。最好的狀況就是一樣的圖片在drawable文件不一樣的dpi層級包中都有適應具體大小的圖片(這樣在運行不一樣設備時,被動縮放比都是1)。不然在設備沒有直接對應的dpi的drawable文件時,會影響被動縮放比,並且大多數時候是致使圖片放大,且致使內存增大二次方。如剛纔的計算過程若是把圖片從drawable-xxhdpi(480dpi)移到drawable文件包(默認160)中,一樣的加載代碼最後內存就會放大9倍。
  • 修改色彩空間:在明確的不須要透明度的狀況,使用RGB_565替換ARGB_8888,能夠直接達到內存縮小一半的功效。缺點就是有限定條件。(從網絡上的博文描述,不推薦使用ARGB_4444替換,由於這樣的圖片質量太差)
相關文章
相關標籤/搜索