Android加載不一樣DPI資源與內存消耗之間的關係

Android DPI 分級標準簡介

Android 設備在物理尺寸和屏幕密度上都有很大的不一樣,爲了簡化多設備的設計方案,就是設定一套分級標準。屏幕密度上的分級標準就是:LDPI、MDPI、HDPI、XHDPI,也就是各類大小的 DPI(Dots per inch)。DPI 就是屏幕像素密度的衡量標準java

不一樣設備共享同一套 DPI 資源有哪些問題?

如今進入正題。 android

Q:很多公司出於簡化設計和研發的目的,每每在方案中只使用一套 DPI 資源,這樣作可不可行呢? 數組

A:Android 有一套加載資源的規則,若是對應的 DPI 文件夾不存在要用的資源就會按照規則去找其它的DPI 文件夾,若是最終能找到就可使用。因此上述方案是「可行的」– 能夠正常運行不報錯的。那可行性的另外一個方面就是對性能有沒有影響。上述問題就變爲下面問題: app

Q:同一套 DPI 資源在不一樣手機上使用時內存消耗有什麼不一樣? 或 App 中加載不一樣 DPI 文件夾中的資源內存消耗有什麼不一樣? 性能

問題:DPI 越小的文件夾內存消耗越大?

下面以 png 圖片的加載爲例。 測試

demo pic

原始圖片(attribute—width:960,height:540,bit depth:32,size:217082bytes)。 spa

作簡單的 demo app,即在 activity 中只加載這一個圖片。 設計

放在 hdpi 文件夾中,dumpsys meminfo 後發現 Heap Alloc 爲 5420,遠遠大於 size,因此先確定的是內存消耗與圖片文件大小無關。 code

再放到不一樣的 DPI 文件夾中發現:越是 DPI 小的文件夾內存消耗越大! 圖片

分析:加載低 DPI 資源會額外拉伸放大圖片

因爲 Heap Alloc 只能看到堆的分配整體大小,不能看到上述發現有什麼「規律」,因此接着使用 MAT 分析。

在 hdpi 中抓取 hprof 文件,用 MAT 打開:

mat

見圖中的 byte 數組,大小爲 2073600,這個大小就是加載的那張 png 圖片佔用的內存大小。

分別分析圖片資源放在 mdpi、ldpi 和 xhdpi 時的 hprof 文件,byte 數組大小分別爲:4665600、8294400、1166400。不一樣 DPI 文件夾與圖片佔用的內存大小關係以下:

DPIs ldpi mdpi hdpi xhdpi
Byte[] size 8294400 4665600 2073600 1166400
Ratio 8^2 6^2 4^2 3^2

開 始就說到 Android 的屏幕密度的分級標準是 LDPI、MDPI、HDPI、XHDPI 這些各類大小的 DPI。也就是 LDPI 的設備默認使用的是 ldpi 文件夾下的資源。根據 DPI 值的大小再整理一下,屏幕像素密度的值對應使用的 DPI 文件夾關係以下:

DPIs ldpi mdpi hdpi xhdpi
Density 120 160 240 320
Ratio 3 4 6 8

根據上面兩個表格的 Ratio 值,能夠發現內存佔用和 DPI 資源是有必定規律的。其實咱們知道 png 加載內存的消耗與文件大小無關,而是與 png 圖片的長寬和位深有關,也就是:

1
Memory Consumption Size(UOM:byte) = Width * Height * (Bit depth / 8)

上面公式是不能徹底在 Android 中使用的,根據上述找到的規律,Android 中 png 圖片內存消耗公式能夠歸納爲:

1
2
3
4
Memory Consumption Size(UOM:byte) = ScaledWidth * ScaledHeight * (Bit depth / 8)
ScaledWidth = Width * factor
ScaledHeight = Height * factor
factor = DENSITY_DEVICE / ResourceDensity  // DENSITY_DEVICE 是設備的 DPI 大小, ResourceDensity 是設備加載的 DPI 文件夾對應的 DPI 大小

因此:

1
Memory Consumption Size = Width * Height * (DENSITY_DEVICE / ResourceDensity)^2 * (Bit depth / 8)

上述 hdpi 中的 2073600 能夠由此計算得出:

1
960 * 540 * (240 / 240)^2 * (32 / 8) = 2073600

在 BitmapFactory.cpp 的 doDecode() 中 添加 log ,可驗證上述公式(資源在 xdpi 中,sx、sy 就是上述公式中的 factor):

1
01-15 21:00:49.479  3079  3079 D BitmapFactory: doDecode----sx:0.750000 ,sy:0.750000 ,scaledWidth:405 ,scaledHeight:720 ,decodingBitmap.width:540 ,decodingBitmap.heigth:960

Android 加載資源默認選用和設備 DPI 匹配的資源,若是沒有就去到其它 DPI 文件夾中尋找資源。找到後它會認爲使用了不一樣 DPI 的資源,爲了保持與設備 DPI 一致,就會對資源作拉伸或縮放處理再加載。下面是上述 png 圖片分別放在 mdpi 和 xxhdpi 文件夾下的截圖:

mdpi xxhdpi
mdpi xxhdpi

很明顯就能夠看到在 xxhdpi 下時的截圖模糊了很多。使用的測試手機是 hdpi 的,可是默認 hdpi 找不到圖片資源,它就會按照必定規則找到我放在 xxhdpi 中的資源。手機認爲從 xxhdpi 獲取的資源比手機的 dpi 要高,它就會按照表格中的比例把資源縮小,也就是加載到內存中的圖片資源已是原來大小的 1/2,佔用的內存固然會縮小不作縮放操做圖片的 1/4。可是壞處也是顯而易見的,顯示到手機的圖片資源清晰度降低,模糊了不少。

相反的,hdpi 的手機加載低 dpi 資源,例如 ldpi,加載到內存前會先按比例拉伸。拉伸後再顯示到手機中清晰度是沒有問題,可是內存佔用確增大爲原來的 4 倍!仍是要注意到這一點,若是圖片資源在 app 中放錯 dpi 文件夾,使用體驗會大打折扣,或者儘可能使用 9patch 圖片。

小結:建議根據設備配置 DPI 資源

如今就能夠回答提出的問題了:

Q:同一套 DPI 資源在不一樣手機上使用時內存消耗有什麼不一樣? 或 App 中加載不一樣 DPI 文件夾中的資源內存消耗有什麼不一樣?

A:不要使用一套資源適用於各類不一樣 DPI 的設備,這樣圖片的清晰度和內存消耗都會有問題。這就是爲何 Android 要求對不一樣 DPI 文件作不一樣的資源,而且不一樣 DPI 資源的長寬比要與 DPI Ratio 相對應。

PS:此文結論一句話就能給說清楚,但推導的過程更重要。愛因斯坦說過:Imagination is more important than knowledge.

參考資料

參考的 Android 源碼:

1
2
3
4
/frameworks/base/core/java/android/util/DisplayMetrics.java 
/frameworks/base/graphics/java/android/graphics/Bitmap.java
/frameworks/base/graphics/java/android/graphics/drawable/BitmapDrawable.java
/frameworks/base/core/jni/android/graphics/BitmapFactory.cpp
相關文章
相關標籤/搜索