Android圖片加載內(nèi)存占用分析

作為一名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)


壓縮前磁盤(pán)占用大小

壓縮前內(nèi)存大小

對(duì)圖片進(jìn)行壓縮后進(jìn)行同樣得操作

壓縮后磁盤(pán)占用大小

壓縮后內(nèi)存占用大小

很明顯,壓縮前后內(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)存大小,是很合理的,但是圖片是放置在


nodpi.png

前面也已經(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,463評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,009評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,778評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,218評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,436評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,795評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,993評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,229評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,659評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,917評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,687評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,990評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容