關于Android中圖片大小、內存占用與drawable文件夾關系的研究與分析

從上一篇文章《Android屏幕適配全攻略》寫完之后,經常會有朋友問我這個問題:“能不能一個App只提供一套切圖適應所有的分辨率呢?”我覺得有必要寫一篇文章來研究一下這個問題,所以就有了這篇文章。

研究內容

本篇內容主要探討以下場景:同一張圖片,放置在不同的drawable文件夾,在同一設備上運行,對圖片大小及內存占用有什么影響。

研究方法

  • 控制變量法
  • 分析法

測試環境

采用錘子T1手機(1080*1960,xxhdpi)進行測試

對于內存的查看,使用AS自帶的內存查看工具。

圖片大小使用如下代碼獲取

 private void printBitmapSize(ImageView imageView) {
        Drawable drawable = imageView.getDrawable();
        if (drawable != null) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            Log.d(TAG, " width = " + bitmap.getWidth() + " height = " + bitmap.getHeight());
        } else {
            Log.d(TAG, "Drawable is null !");
        }
    }

研究過程

下面將給出測試的過程的截圖,然后進行分析和總結

下面的測試使用的是一張720*1280分辨率的png圖片,32位色,占用硬盤大小為77.11k

下面給出測試工程代碼,非常簡單

主界面

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private ImageView img;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        img = (ImageView) findViewById(R.id.img);
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        printBitmapSize(img);
    }

    private void printBitmapSize(ImageView imageView) {
        Drawable drawable = imageView.getDrawable();
        if (drawable != null) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            Bitmap bitmap = bitmapDrawable.getBitmap();
            Log.d(TAG, " width = " + bitmap.getWidth() + " height = " + bitmap.getHeight());
        } else {
            Log.d(TAG, "Drawable is null !");
        }
    }

}

布局界面

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

在不設置圖片的情況下,App占用內存8.31M

以下為測試部分

把圖片放置在drawable文件夾下,圖片大小為2160 * 3840,內存占用39.88M

把圖片放置在drawable-mdpi文件夾下,圖片大小為2160 * 3840,內存占用39.84M

把圖片放置在drawable-hdpi文件夾下,圖片大小為1440 * 2560,內存占用22.26M

把圖片放置在drawable-xhdpi文件夾下,圖片大小為1080 * 1920,內存占用16.11M

把圖片放置在drawable-xxhdpi文件夾下,圖片大小為720 * 1280,內存占用11.86M

把圖片放置在drawable-xxxhdpi文件夾下,圖片大小為540 * 960,內存占用10.29M

結果分析

從上面的測試結果,我們可以得出如下結論:

  1. 同一張圖片,放在不同目錄下,會生成不同大小的Bitmap
  2. Bitmap的長度和寬度越大,占用的內存就越大
  3. 圖片在硬盤上占用的大小,與在內存中占用的大小完全不一樣

下面我會對上面幾個問題一一解釋。

我們以放在drawable文件夾下面的圖片為例,加載到內存之后,2160*3840大小的Bitmap占用的內存為

2160 * 3840 * 4 = 3317,7600 byte = 3,2400kb = 31.640625 M

所以drawable文件夾下的App內存占用 = 原始內存8.31M+圖片內存31.64M= 39.95M ,與實際內存占用39.88M存在0.1755%的誤差,在誤差范圍之內。

先簡單解釋一下上面的計算公式,長*寬是圖片的像素總數,乘以4則是因為一個像素占用A、R、G、B四個通道,每個通道占用8位,所以描述一個像素需要32位即4個字節。

一個顏色通道需要8位描述,2^8=256,所以每個顏色通道就有256種狀態。如果把彩色圖轉化成灰階圖的話,也有256種狀態分割從白色到黑色之間的過渡顏色。

當然,也并不是所有格式的圖片每個像素占用4字節,這和圖片在加載時設置的Bitmap.Config有關,默認的是Bitmap.Config.ARGB_8888,其他類型如下:

  • Bitmap.Config.ALPHA_8 此時圖片只有alpha值,沒有RGB值,
    一1個像素占用一個字節
  • Bitmap.Config.ARGB_4444 一個像素占用2個字節,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4個bites共16bites,即2個字節
  • Bitmap.Config.ARGB_8888 一個像素占用4個字節,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8個bites,共32bites,即4個字節。這是一種高質量的圖片格式,在電腦上普通采用。它也是Android手機上一個Bitmap的默認格式。
  • Bitmap.Config.RGB_565 一個像素占用2個字節,沒有alpha(A)值,即不支持透明和半透明,Red(R)值占5個bites ,Green(G)值占6個bites ,Blue(B)值占5個bites,共16bites,即2個字節。對于沒有透明和半透明顏色的圖片來說,該格式的圖片能夠達到比較的呈現效果,相對于ARGB_8888來說也能減少一半的內存開銷。因此它是一個不錯的選擇。

那么為啥在硬盤上存儲只需要77.11k,放到內存里面就需要30多M呢?

因為這根本不是一回事呀~

存放在硬盤上的圖片文件,會根據各自的壓縮規則進行壓縮,比如Jpeg這種有損壓縮的圖片格式,最常使用可變字長編碼的哈弗曼編碼,會使用哈弗曼樹,也就是最優二叉樹,根據某些數據出現的頻率對數據段編碼,從而減少占用的硬盤大小。

比如說“10111”這個序列在圖片的二進制數據中出現的概率最大,那我們可以用“01”來代替這一段數據,原來5位的數據,用2位就可以表示了,這就是壓縮率60%。當然這只是打個比方,在實際操作中需要考慮“異前綴原則”等編碼的基本原則。

而如果把圖像讀取到內存中就不一樣了,因為我們需要每一個像素都能在屏幕上顯示,所以會把每個像素點都加載至內存中,不會對相同像素進行壓縮或者是替換,所以你也應該能明白前面提到的Bitmap占用內存大小的計算公式的由來了。

說到這里,其實后兩個結論已經解釋清楚了,那么為什么“同一張圖片,放在不同目錄下,會生成不同大小的Bitmap”呢?

如果你真的看懂了我之前寫的文章,那么這個問題應該不算問題。

我的測試設備為錘子T1,1080*1960,xxhdpi,所以說,如果把這張放置在xxhdpi的話,應該不會對圖像進行放縮,也就是原始大小,所以我們在前面得到drawable-xxhdpi文件夾下,圖片大小為720 * 1280是完全可以理解的,就是圖片本身的大小。

當圖片放置在drawable-hdpi中時,圖片大小為1440 * 2560,長寬變為原來的兩倍,這是因為不同分辨率之間的倍數關系導致的,來一張圖

我們可以很明顯的看到xxhdpi是hdpi的2倍,所以如果單獨放置在某個drawable文件夾,手機會自動根據當前的屏幕密度對圖片進行放縮。

比如上面,當把圖片放置在xxxhdpi里面的時候,在xxhdpi的設備上,圖片長 = 720 * (3/4) = 540,圖片寬 = 1280 * (3/4) = 960,這與上面的測試結果是完全一致的。

至于為什么在前面的測試中,drawable和drawable-mdpi是一樣的大小,是因為drawable-mdpi是系統默認的像素密度,其他像素密度都以它為基數,當只在drawable中存在圖片時,如果使用該圖片,那么將按照drawable-mdpi的放縮比例進行放縮。

結論

從上面的測試我們可以得出以下幾個結論:

  1. 當圖片放置在不同drawable文件夾中,且只有這一張圖片時,運行設備會根據自身的屏幕密度,對圖片進行放縮,放縮比例符合前面圖上的規則
  2. 圖片文件的大小與在內存中占用的大小沒關系,內存中實際占用大小與圖片分辨率、像素顯示參數有關

所以,在一個App里面使用一套UI理論上應該是沒有問題的,但是要注意

  • 最好使用較高分辨率的切圖,并且放置在正確的drawable文件夾中,比如按照xxhdpi的分辨率進行切圖,放置在drawable-xxhdpi中
  • 對于可以使用.9格式的圖片,最好使用.9,減少資源大小
  • 如果有條件,最好提供多套UI切圖。如果只有一套切圖,系統需要對圖片進行壓縮,會進行大量運算,影響設備性能。同時,在某些情況下,系統對圖片的壓縮會可能會出現鋸齒,造成信息的丟失
  • 如果是多套切圖的話,最好不要直接用工具按照比例放縮,這樣小圖標會丟失一些細節。當然,這部分是美工來做的,可以讓她參考這篇文章利用PS CS6的新功能保持ICON細節飽滿完美

思考一下,如果把一個本來應該放在drawable-xxhdpi里面的圖片放在了drawable文件夾中會出現什么問題呢?

在xxhdpi設備上,圖片會被放大3倍,圖片內存占用就會變為原來的9倍!

另外一個難以解釋的問題

我還試著將上面那張圖片放置在drawable-xxhdpi文件夾下,觀察在不同屏幕密度設備的表現

xxhdpi設備(T1),圖片大小為720 * 1280,內存占用 = 11.86M-8.31M= 3.55M

xxhdpi設備(N5) ,圖片大小為720 * 1280,內存占用 = 20.19M-16.82M = 3.37M

xxhdpi設備(魅族m351),圖片大小為720 * 1280,內存占用 = 20.17M-17.84M = 2.33M

xhdpi設備(華為g716-l070) 內存占用 = 7.72M-2.66M = 5.06M

hdpi設備(聯想A360e),圖片大小為360 * 640,內存占用 = 2.83M-2.96M = -0.7M

hdpi設備(酷派7269),圖片大小為320 * 568,內存占用 = 6.85M-2.78M = 4.07M

然后我就凌亂了,這是什么玩意!除了俺的大錘子和原生的N5符合圖片內存計算公式,其他的設備都是什么玩意!聯想手機的還是負值!不過我在模擬機上測試得到的也是類似的效果,這個測試與上面的應該是同一種情況,具體原因不得而已。

有知曉原因的可以搞告訴我,謝謝。


尊重原創,轉載請注明:From 凱子哥(http://blog.csdn.net/zhaokaiqiang1992) 侵權必究!

關注我的微博,可以獲得更多精彩內容 http://weibo.com/zhaokaiqiang1992

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,367評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,001評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,213評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,535評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,317評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,868評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,963評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,090評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,599評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,549評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,712評論 1 367
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,233評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,353評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,607評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,321評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,686評論 2 370

推薦閱讀更多精彩內容