Android中Bitmap占用內(nèi)存計(jì)算

在Android開發(fā)中吴藻,我們經(jīng)常會(huì)是用到Bitmap,但是這個(gè)是消耗內(nèi)存的主,因此我們?cè)谑褂脮r(shí),要弄清楚他到底占用多少內(nèi)存,今天就來研究下怎么算出Bitmap占用多少內(nèi)存丧肴。
首先我找了一張400 * 400的圖片,然后放在drawable-hdpi吆你、drawable-xhdpi文件夾中

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.header_image_hdpi);
Log.d("fishpan_log", "MainActivity.onCreate: hdpi -> " + bitmap.getByteCount());

bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.header_image_xhdpi);
Log.d("fishpan_log", "MainActivity.onCreate: xhdpi -> " + bitmap.getByteCount());

運(yùn)行結(jié)果

MainActivity.onCreate: hdpi -> 2149156
MainActivity.onCreate: xhdpi -> 1210000

下面我就來看下bitmap.getByteCount的方法實(shí)現(xiàn).

public final int getByteCount() {
    return getRowBytes() * getHeight();
}

每行占用的字節(jié)數(shù)*高度

public final int getRowBytes() {
    ...省略代碼
    return nativeRowBytes(mNativePtr);
}

這里的nativeRowBytes是一個(gè)native方法,我們可以在/frameworks/base/core/jni/android/graphics/Bitmap.cpp中找到對(duì)應(yīng)的實(shí)現(xiàn)尊沸。

static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
    return bitmap->rowBytes();
}

我們?cè)倏聪耂kBitmap是個(gè)什么鬼威沫?

size_t rowBytes() const { return fRowBytes; }

在SkBitmap.cpp中發(fā)現(xiàn)fRowBytes是通過ComputeRowBytes計(jì)算的

int SkBitmap::ComputeRowBytes(Config c, int width) {
    if (width < 0) {
        return 0;
    }
    
    Sk64 rowBytes;
    rowBytes.setZero();
    
    switch (c) {
        case kNo_Config:
        case kRLE_Index8_Config:
            break;
        case kA1_Config:
            rowBytes.set(width);
            rowBytes.add(7);
            rowBytes.shiftRight(3);
            break;
        case kA8_Config:
        case kIndex8_Config:
            rowBytes.set(width);
            break;
        case kRGB_565_Config:
        case kARGB_4444_Config:
            rowBytes.set(width);
            rowBytes.shiftLeft(1);
            break;
        case kARGB_8888_Config:
            rowBytes.set(width);
            rowBytes.shiftLeft(2);
            break;
        default:
            SkASSERT(!"unknown config");
            break;
    }
    return isPos32Bits(rowBytes) ? rowBytes.get32() : 0;
}

這里就是這對(duì)圖片格式進(jìn)行的計(jì)算了,ARGB_8888格式圖片就是寬度 * 4贤惯,整個(gè)Bitmap占用的就是寬度 * 高度 * 4 byte;

問題來了,圖片的高度寬度是怎么得到棒掠?通過上面demo我們可以明顯看出孵构,圖片在內(nèi)存中的大小并不是原始大小,而是發(fā)生了變化烟很。
看下BitmapFactory.decodeResource方法

public static Bitmap decodeResourceStream(Resources res, TypedValue value,
            InputStream is, Rect pad, Options opts) {
    if (opts == null) {
        opts = new Options();
    }

    if (opts.inDensity == 0 && value != null) {
        final int density = value.density;
        if (density == TypedValue.DENSITY_DEFAULT) {
            opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
        } else if (density != TypedValue.DENSITY_NONE) {
            opts.inDensity = density;
        }
    }
    
    if (opts.inTargetDensity == 0 && res != null) {
        opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
    }
    
    return decodeStream(is, pad, opts);
}

首先實(shí)例化了一個(gè)Options類颈墅,并設(shè)置了inDensity、inTargetDensity屬性雾袱,inDensity與圖片所在文件夾相關(guān)恤筛,inTargetDensity就是設(shè)備的屏幕密度。

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
    ...省略部分代碼
    try {
        if (is instanceof AssetManager.AssetInputStream) {
            final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
            bm = nativeDecodeAsset(asset, outPadding, opts);
        } else {
            bm = decodeStreamInternal(is, outPadding, opts);
        }

    if (bm == null && opts != null && opts.inBitmap != null) {
        throw new IllegalArgumentException("Problem decoding into existing bitmap");
    }
    ...省略部分代碼
    setDensityFromOptions(bm, opts);
    return bm;
}

中間會(huì)調(diào)用nativeDecodeAsset或者decodeStreamInternal方法芹橡,但是最終都會(huì)調(diào)用到BitmapFactory.cpp中的doDecode方法

static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
        jobject options, bool allowPurgeable, bool forcePurgeable = false) {


    if (options != NULL) {

        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
            const int density = env->GetIntField(options, gOptions_densityFieldID);
            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
            if (density != 0 && targetDensity != 0 && density != screenDensity) {
                scale = (float) targetDensity / density;
            }
        }
    }


    int scaledWidth = decodingBitmap.width();
    int scaledHeight = decodingBitmap.height();

    if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
        scaledWidth = int(scaledWidth * scale + 0.5f);
        scaledHeight = int(scaledHeight * scale + 0.5f);
    }

    // update options (if any)
    if (options != NULL) {
        env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
        env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
        env->SetObjectField(options, gOptions_mimeFieldID,
                getMimeTypeString(env, decoder->getFormat()));
    }
}

省略一堆代碼毒坛,看重點(diǎn)。中間計(jì)算scale就是targetDensity / density,圖片最終的寬度和高度就是scaledWidth = int(scaledWidth * scale + 0.5f); scaledHeight = int(scaledHeight * scale + 0.5f);
OK林说,現(xiàn)在我們就知道一個(gè)圖片占多大內(nèi)存了煎殷,我們通過自己的計(jì)算看看是不是符合上邊的結(jié)果.

我的手機(jī)是屏幕密度是440
圖片位置:drawable-hdpi
圖片格式:ARGB-8888
圖片大小:400 * 400
寬度:int(400 * (440 / 240) + 0.5)
高度:int(400 * (440 / 240) + 0.5)
內(nèi)存大型嚷帷:2149156

圖片位置:drawable-xhdpi
圖片格式:ARGB-8888
圖片大泻乐薄:400 * 400
寬度:int(400 * (440 / 320) + 0.5)
高度:int(400 * (440 / 320) + 0.5)
內(nèi)存大小:1210000

總結(jié):通過上面的分析我們可以發(fā)現(xiàn)珠移,圖片占用內(nèi)存大小和手機(jī)的密度成正比弓乙,所在文件夾密度成反比

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市剑梳,隨后出現(xiàn)的幾起案子唆貌,更是在濱河造成了極大的恐慌,老刑警劉巖垢乙,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锨咙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡追逮,警方通過查閱死者的電腦和手機(jī)酪刀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钮孵,“玉大人骂倘,你說我怎么就攤上這事“拖” “怎么了历涝?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我荧库,道長(zhǎng)堰塌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任分衫,我火速辦了婚禮场刑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚪战。我一直安慰自己牵现,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布邀桑。 她就那樣靜靜地躺著瞎疼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪概漱。 梳的紋絲不亂的頭發(fā)上丑慎,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音瓤摧,去河邊找鬼竿裂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛照弥,可吹牛的內(nèi)容都是我干的腻异。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼这揣,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼悔常!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起给赞,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤机打,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后片迅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體残邀,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年柑蛇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芥挣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耻台,死狀恐怖空免,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情盆耽,我是刑警寧澤蹋砚,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布扼菠,位于F島的核電站,受9級(jí)特大地震影響都弹,放射性物質(zhì)發(fā)生泄漏娇豫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一畅厢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧氮昧,春花似錦框杜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至椎组,卻和暖如春油狂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寸癌。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工专筷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒸苇。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓磷蛹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親溪烤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子味咳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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