Android 設備在物理尺寸和屏幕密度上都有很大的不一樣,爲了簡化多設備的設計方案,就是設定一套分級標準。屏幕密度上的分級標準就是:LDPI、MDPI、HDPI、XHDPI,也就是各類大小的 DPI(Dots per inch)。DPI 就是屏幕像素密度的衡量標準。 java
如今進入正題。 android
Q:很多公司出於簡化設計和研發的目的,每每在方案中只使用一套 DPI 資源,這樣作可不可行呢? 數組
A:Android 有一套加載資源的規則,若是對應的 DPI 文件夾不存在要用的資源就會按照規則去找其它的DPI 文件夾,若是最終能找到就可使用。因此上述方案是「可行的」– 能夠正常運行不報錯的。那可行性的另外一個方面就是對性能有沒有影響。上述問題就變爲下面問題: app
Q:同一套 DPI 資源在不一樣手機上使用時內存消耗有什麼不一樣? 或 App 中加載不一樣 DPI 文件夾中的資源內存消耗有什麼不一樣? 性能
下面以 png 圖片的加載爲例。 測試
原始圖片(attribute—width:960,height:540,bit depth:32,size:217082bytes)。 spa
作簡單的 demo app,即在 activity 中只加載這一個圖片。 設計
放在 hdpi 文件夾中,dumpsys meminfo 後發現 Heap Alloc 爲 5420,遠遠大於 size,因此先確定的是內存消耗與圖片文件大小無關。 code
再放到不一樣的 DPI 文件夾中發現:越是 DPI 小的文件夾內存消耗越大! 圖片
因爲 Heap Alloc 只能看到堆的分配整體大小,不能看到上述發現有什麼「規律」,因此接着使用 MAT 分析。
在 hdpi 中抓取 hprof 文件,用 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 |
很明顯就能夠看到在 xxhdpi 下時的截圖模糊了很多。使用的測試手機是 hdpi 的,可是默認 hdpi 找不到圖片資源,它就會按照必定規則找到我放在 xxhdpi 中的資源。手機認爲從 xxhdpi 獲取的資源比手機的 dpi 要高,它就會按照表格中的比例把資源縮小,也就是加載到內存中的圖片資源已是原來大小的 1/2,佔用的內存固然會縮小不作縮放操做圖片的 1/4。可是壞處也是顯而易見的,顯示到手機的圖片資源清晰度降低,模糊了不少。
相反的,hdpi 的手機加載低 dpi 資源,例如 ldpi,加載到內存前會先按比例拉伸。拉伸後再顯示到手機中清晰度是沒有問題,可是內存佔用確增大爲原來的 4 倍!仍是要注意到這一點,若是圖片資源在 app 中放錯 dpi 文件夾,使用體驗會大打折扣,或者儘可能使用 9patch 圖片。
如今就能夠回答提出的問題了:
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
|