作為一名Android開(kāi)發(fā)人員,你見(jiàn)得最多的大概就是res/drawable-[density]/ 文件夾了,現(xiàn)在又大概多了 res/mipmap-[density]/ 文件夾,這些文件夾通常用來(lái)存放圖片資源文件,大家可能再熟悉不過(guò)了,現(xiàn)在我問(wèn)你,一張大小為376.16K的480x800且位數(shù)為8的圖片放在res/drawable-xxhdpi/ 文件夾下,在分辨率為1920*1080的手機(jī)上這張圖片占用的內(nèi)存是多少?
1 概念厘清
如果對(duì)此比較有了解的小傻逼們,后面其實(shí)不需要看了,純粹來(lái)掃一下盲,在正式分析之前,先來(lái)厘清一下相關(guān)的概念。
- 1.1屏幕尺寸:按屏幕對(duì)角測(cè)量的實(shí)際物理尺寸,例如5.5英寸。Android 將所有實(shí)際屏幕尺寸分組為四種通用尺寸:小、 正常、大和超大;
- 1.2分辨率:屏幕上物理像素的總數(shù),添加對(duì)多種屏幕的支持時(shí), 應(yīng)用不會(huì)直接使用分辨率,而只應(yīng)關(guān)注通用尺寸和密度組指定的屏幕尺寸及密度;
- 1.3屏幕密度:屏幕物理區(qū)域中的像素量,通常稱為 dpi(每英寸點(diǎn)數(shù))。屏幕密度越低在給定物理區(qū)域的像素就會(huì)較少。Android 將所有屏幕密度分為六組通用密度:ldpi( 低)、mdpi(中)、hdpi(高)、xhdpi(超高)、xxhdpi(超超高)和xxxhdpi(超超超高);
- 1.4密度無(wú)關(guān)像素 (dp):在定義 UI 布局時(shí)應(yīng)使用的虛擬像素單位。密度無(wú)關(guān)像素等于 160 dpi 屏幕上的一個(gè)物理像素,這是系統(tǒng)為mdpi(中)密度屏幕假設(shè)的基線密度。在運(yùn)行時(shí),系統(tǒng)根據(jù)使用中屏幕的實(shí)際密度按需要以透明方式處理dp單位的任何縮放 。dp單位轉(zhuǎn)換為屏幕像素很簡(jiǎn)單: px = dp * (dpi / 160)。 例如,在 240 dpi屏幕上,1 dp等于1.5 物理像素。
對(duì)于我們的分析比較重要的就是屏幕密度。
2 屏幕密度(dpi)對(duì)應(yīng)關(guān)系
通用密度 | ldpi | mdpi(基線密度) | hdpi | xhdpi | xxhdpi | xxxhdpi |
---|---|---|---|---|---|---|
描述 | 低 | 中 | 高 | 超高 | 超超高 | 超超超高 |
大小(單位dpi) | 120 | 160 | 240 | 320 | 480 | 640 |
縮放系數(shù) | 0.75 | 1 | 1.5 | 2 | 3 | 4 |
六種通用密度之間遵循 3:4:6:8:12:16 的縮放比率,要注意的一點(diǎn)是xxxhdpi僅限啟動(dòng)器圖標(biāo)。
3 具體分析實(shí)現(xiàn)代碼
代碼很簡(jiǎn)單,就是用一個(gè)ImageView包含一張背景圖片,然后通過(guò)轉(zhuǎn)換為Bitmap查看占用內(nèi)存大小。
布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.xishuang.imagesizetest.MainActivity">
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/bg2" />
</FrameLayout>
布局文件,就是一個(gè)ImageView控件,包含一張背景圖。
MainAcivity.java
private void printBitmapSize(ImageView imageView) {
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
//API 19
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
size = bitmap.getAllocationByteCount();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1){
//API 12
size = bitmap.getByteCount();
} else {
//earlier version
size = bitmap.getRowBytes() * bitmap.getHeight();
}
Log.d(TAG, " size = " + size);
} else {
Log.d(TAG, "Drawable is null !");
}
}
getAllocationByteCount()方法可以獲取圖片的實(shí)際占用內(nèi)存大小,在此之前得介紹一種特殊的res/drawable-[density]/文件夾,就是res/drawable-nodpi/,不管當(dāng)前屏幕的密度如何,系統(tǒng)都不會(huì)縮放以此限定符標(biāo)記的資源。意思就是在這個(gè)文件夾中的圖片按原樣進(jìn)行展示,不會(huì)像其它的res/drawable-[density]/那樣改變文件的大小,我們就以此為基準(zhǔn)進(jìn)行分析。
4 圖片的實(shí)際內(nèi)存占用
以實(shí)際例子作為分析,把一張大小為376.16K的480x800且位數(shù)為8的圖片為例
圖片的像素總數(shù) 480x800 = 384000
先使用壓縮前的圖片為例,分析圖片占用的內(nèi)存大小并打印出來(lái)
對(duì)圖片進(jìn)行壓縮后進(jìn)行同樣得操作
很明顯,壓縮前后內(nèi)存的占用大小同樣為1536000(Byte),說(shuō)明圖片的磁盤(pán)占用大小與圖片的內(nèi)存或顯存占用沒(méi)有必然關(guān)系。從而說(shuō)明壓縮圖片可以減少我們得apk大小,但是內(nèi)存的占用是不會(huì)變小的,那么圖片的內(nèi)存占用與什么有關(guān)系呢?繼續(xù)。。。
圖片的內(nèi)存占用大小為1536000(Byte),而圖片的原始圖片像素總數(shù)為384000,一眼看過(guò)去好像沒(méi)啥關(guān)系,但是真相是384000 * 4 = 1536000(Byte),原始圖片尺寸大小與最終的內(nèi)存占用大小呈倍數(shù)的關(guān)系,所以在這里與內(nèi)存占用大小有直接關(guān)系的就是原始圖片尺寸大小(例如:480x800),道理我都懂,但是倍數(shù)關(guān)系是從哪里來(lái)的呢,這就要談?wù)摰紹itmap的像素格式了。
Android系統(tǒng)支持4種格式的像素格式,源碼在Bitmap.Config中
/**
* 可用的bitmap配置, 一個(gè)bitmap配置描述的是每個(gè)像素的存儲(chǔ)格式,這將會(huì)影響到圖片的質(zhì)量 (顏色深
* 度) 以及顯示透明/半透明顏色的能力
*/
public enum Config {
// 這些枚舉中的值必須要與Skia圖像引擎的SkBitmap.h中對(duì)應(yīng)值一一對(duì)應(yīng)
/**
* 只有一個(gè)alpha通道
* 每個(gè)像素占1個(gè)字節(jié)
*/
ALPHA_8 (1),
/**
*每個(gè)像素占用2個(gè)字節(jié),只有RGB 3個(gè)通道,沒(méi)有alpha 通道
* 紅色的精度是5 bits, 綠色精度是6 bits,藍(lán)色精度是5
*/
RGB_565 (3),
/**
* 每個(gè)像素占用2個(gè)字節(jié).
* (雖然占用內(nèi)存只有 ARGB8888 的一半,不過(guò)已經(jīng)被官方嫌棄)
*/
@Deprecated
ARGB_4444 (4),
/**
* 每個(gè)像素占用4個(gè)字節(jié). 每個(gè)通道 (RGB的3個(gè)通道和alpha
* 的1個(gè)透明度通道) 的進(jìn)度是8bit (256個(gè)可能值)
* 這種配置是最靈活的, 質(zhì)量最好,盡量使用這種格式.
*/
ARGB_8888 (5);
}
由于官方默認(rèn)使用ARGB_8888格式,導(dǎo)致圖片的每個(gè)像素會(huì)占用4個(gè)Byte大小,所以最終的圖片占用內(nèi)存大小就是像素總數(shù)*像素格式,放到例子里頭就是384000 * 4 = 1536000(Byte),成功接上去了,哈哈哈。。。
小結(jié)論:圖片的直接內(nèi)存占用和圖片的像素總數(shù)和系統(tǒng)的像素格式相關(guān),與磁盤(pán)存儲(chǔ)的圖片大小無(wú)關(guān),其實(shí)與磁盤(pán)存儲(chǔ)的圖片位數(shù)也無(wú)關(guān)。
5 Android對(duì)在res/drawable-[density]/ 文件夾中圖片進(jìn)行的騷操作
前面提到的圖片實(shí)際占用內(nèi)存大小,是很合理的,但是圖片是放置在
前面也已經(jīng)提到過(guò)res/drawable-nodpi/文件夾,在這個(gè)文件夾中的圖片按原樣進(jìn)行展示,不會(huì)像其它的res/drawable-[density]/那樣改變文件的大小,類似于從SD卡或者網(wǎng)絡(luò)直接加載一張圖片。
但是如果把圖片放在其它的res/drawable-[density]/ 文件夾中的話,事情就會(huì)變得有些不一樣了,系統(tǒng)會(huì)根據(jù)手機(jī)的屏幕密度來(lái)縮放對(duì)應(yīng)文件夾中的圖片。
下面就是測(cè)試結(jié)果,測(cè)試手機(jī)為360 vizza,手機(jī)分辨率為1920*1080,屏幕密度為480dpi,測(cè)試圖片為480x800的圖片。
先把圖片放置drawable-ldpi中看占用內(nèi)存大小,然后依次類比,得出最終的對(duì)比數(shù)據(jù)。
文件夾 | 文件夾dpi | size(Byte) |
---|---|---|
drawable-ldpi | 120 | 24576000 |
drawable-mdpi | 160 | 13824000 |
drawable-hdpi | 240 | 6144000 |
drawable-xhdpi | 320 | 3456000 |
drawable-xxhdpi | 480 | 1536000 |
drawable-xxxhdpi | 640 | 864000 |
看到這個(gè)結(jié)果先不要慌,穩(wěn)住,我們能贏...
經(jīng)過(guò)前面的分析,我們知道在res/drawable-nodpi/下圖片的占用內(nèi)存為1536000(Byte),發(fā)現(xiàn)沒(méi)有,我加粗的那一行數(shù)據(jù)中,也就在當(dāng)圖片放置在res/drawable-xxhdpi/文件夾下面時(shí),圖片所占用的內(nèi)存也是1536000(Byte),而我們得測(cè)試機(jī)的屏幕密度就是480dpi,說(shuō)明在對(duì)應(yīng)屏幕密度的文件下獲取圖片時(shí)內(nèi)存占用不會(huì)有變化。
而在把圖片放置其他對(duì)應(yīng)dpi文件夾下時(shí),會(huì)出現(xiàn)圖片內(nèi)存占用出現(xiàn)不同程度的縮放,我們稱與手機(jī)屏幕密度一致的文件夾稱之為目標(biāo)文件夾,當(dāng)圖片放置的文件夾對(duì)應(yīng)密度比目標(biāo)文件夾越小時(shí),圖片占用內(nèi)存越大,當(dāng)圖片放置的文件夾對(duì)應(yīng)密度比目標(biāo)文件夾越大時(shí),圖片占用內(nèi)存越小。
還記得這個(gè)表嗎
通用密度 | ldpi | mdpi(基線密度) | hdpi | xhdpi | xxhdpi | xxxhdpi |
---|---|---|---|---|---|---|
描述 | 低 | 中 | 高 | 超高 | 超超高 | 超超超高 |
大小(單位dpi) | 120 | 160 | 240 | 320 | 480 | 640 |
縮放系數(shù) | 0.75 | 1 | 1.5 | 2 | 3 | 4 |
六種通用密度之間遵循 3:4:6:8:12:16 的縮放比率,內(nèi)存占用縮放的秘密其實(shí)就是在這個(gè)縮放比率當(dāng)中,最終的圖片占用內(nèi)存大小為:
圖片最終內(nèi)存=圖片原始內(nèi)存 * (手機(jī)屏幕密度/資源圖片文件密度) ^ 2
其實(shí)就是圖片寬和高都按縮放比率進(jìn)行對(duì)應(yīng)的縮放。
舉個(gè)栗子:
當(dāng)圖片放置在res/drawable-ldpi/文件夾下時(shí),圖片內(nèi)存為1536000(480/120)^2=153600016=24576000(Byte);
當(dāng)圖片放置在res/drawable-xxhdpi/文件夾下時(shí),圖片內(nèi)存為1536000(480/480)^2=15360001=1536000(Byte);
當(dāng)圖片放置在res/drawable-xxxhdpi/文件夾下時(shí),圖片內(nèi)存為1536000(480/640)^2=15360000.5625=864000(Byte);
注:res/drawable-xxxhdpi/文件夾官方建議只能放啟動(dòng)圖標(biāo),這里只是為了測(cè)試才放置測(cè)試圖片。
對(duì)比一下上表對(duì)比數(shù)據(jù),都一一對(duì)應(yīng),說(shuō)明是ok的。
然后最終結(jié)論就是
1、圖片的直接內(nèi)存占用和圖片的像素總數(shù)和系統(tǒng)的像素格式相關(guān),與磁盤(pán)存儲(chǔ)的圖片大小無(wú)關(guān),其實(shí)與磁盤(pán)存儲(chǔ)的圖片位數(shù)也無(wú)關(guān),圖片的直接內(nèi)存占用大小為:像素總數(shù) * 像素的格式(像素的格式其實(shí)就是確定了每個(gè)像素占用的字節(jié)數(shù))
2、圖片放置在res/drawable-[density]/ 文件夾中時(shí),圖片占用內(nèi)存大小為:圖片最終內(nèi)存 = 圖片原始內(nèi)存 * (手機(jī)屏幕密度/資源圖片文件密度) ^ 2