占了多大內(nèi)存?
首先這里要明確的是
占用內(nèi)存
圖片大小
以上兩者是不同的,占用內(nèi)存表示圖片被加載進(jìn)來(lái)以后占用的內(nèi)存空間大小,圖片大小則是圖片在磁盤存儲(chǔ)時(shí)占用的大小。
兩者之間有什么關(guān)系么?下面再講。
獲取一個(gè)biemap占用多大內(nèi)存空間的方法如下:
int sizeOf = bitmap.getRowBytes() * bitmap.getHeight();
//getRowBytes 表示一行的字節(jié)數(shù),getHeight可以認(rèn)為總共有多少行。
舉個(gè)例子
現(xiàn)在有一個(gè)1080*1920大小26.6K的圖片放在xxhdpi文件夾中加載到內(nèi)存中,獲取到的大小為:
8294400 B = 7.9 MB
為什么會(huì)這么大?
實(shí)際上是因?yàn)? 1080 * 1920 * 4
為什么要乘以四,因?yàn)閳D片的大小不但和像素有關(guān)還和色彩格式有關(guān)ARGB_8888一個(gè)像素占用四個(gè)字節(jié)。
同時(shí)還與圖片所在的資源目錄有關(guān)
測(cè)試手機(jī)為 1+3T 1920 * 1080 5.5寸 像素密度為 400 ==> xxhdpi,
而如果放在xhdpi下 則為 1080 * 1.5 * 1920 * 1.5 *4 = 18662400 至于為什么這么算請(qǐng)看下面。
怎么計(jì)算的
先說(shuō)結(jié)論Bitmap在內(nèi)存當(dāng)中占用的大小其實(shí)取決于以下三點(diǎn):
- 色彩格式
- 原始文件存放的資源目錄
- 目標(biāo)屏幕的密度
Bitmap 的生成
關(guān)于 bitmap 的原理網(wǎng)上有很多文章這里就不在講了,可以參考這里,其中在生成過(guò)程中有下面的關(guān)鍵代碼。
outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
colorType, decodingBitmap.alphaType()));
可以看到最終輸出的 outputBitmap 的寬高分別為是 scaledWidth、scaledHeight,把這兩個(gè)變量計(jì)算的片段如下:
if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
scaledWidth = int(scaledWidth * scale + 0.5f);
scaledHeight = int(scaledHeight * scale + 0.5f);
}
其中 scale 的大小是這樣得來(lái)的:
if (density != 0 && targetDensity != 0 && density != screenDensity) {
scale = (float) targetDensity / density;
}
scale 是由 density 和 targetDensity 得來(lái)的,density 是 decodingBitmap 的 density,這個(gè)值跟這張圖片的放置的目錄有關(guān),targetDensity 是目標(biāo)的 density,它的值和當(dāng)前設(shè)備的屏幕密度有關(guān),比如例子中的1+3T就屬于 xxhdpi 則值為480。
到這里我們就知道一個(gè)圖片的寬高值了,那圖片的大小是不是就是 width * height 呢?顯然不是,接著往下看。
獲取大小
獲取大小的方法在最上面我們就講了,獲取一個(gè)biemap占用多大內(nèi)存空間的方法如下:
int sizeOf = bitmap.getRowBytes() * bitmap.getHeight();
//getRowBytes 表示一行的字節(jié)數(shù),getHeight可以認(rèn)為總共有多少行。
getHeight 就是獲取高度單位是(px)高度的計(jì)算方法上面已經(jīng)講了,getRowBytes 又是什么?
public final int getRowBytes() {
if (mRecycled) {
Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
}
return nativeRowBytes(mFinalizer.mNativeBitmap);
}
可以看到它最終到 jni 里面了。
{ "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes }
static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
return static_cast<jint>(bitmap->rowBytes());
}
可以看到 bitmap 的實(shí)際類型是一個(gè) SkBitmap,
/** Return the number of bytes between subsequent rows of the bitmap. */
size_t rowBytes() const { return fRowBytes; }
/** SKbitmap.cpp */
size_t SkBitmap::ComputeRowBytes(Config c, int width) {
return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width);
}
SkImageInfo.h
// 不同色彩格式所占的字節(jié)數(shù)
static int SkColorTypeBytesPerPixel(SkColorType ct) {
static const uint8_t gSize[] = {
0, // Unknown
1, // Alpha_8
2, // RGB_565
2, // ARGB_4444
4, // RGBA_8888
4, // BGRA_8888
1, // kIndex_8
};
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1),
size_mismatch_with_SkColorType_enum);
SkASSERT((size_t)ct < SK_ARRAY_COUNT(gSize));
return gSize[ct];
}
// getRowBytes 的返回值
static inline size_t SkColorTypeMinRowBytes(SkColorType ct, int width) {
return width * SkColorTypeBytesPerPixel(ct);
}
最終可以追蹤到 getRowBytes 的值為 寬度 * 字節(jié)數(shù)。
所以呢大小怎么算?
所以可以得出一個(gè)公式,一個(gè)本地資源的圖片放在內(nèi)存中的大小為:
int(width * targetDensity/density + 0.5f) * int(height * targetDensity/density + 0.5) * gSize
targetDensity 為當(dāng)前設(shè)備的 dpi,density 為圖片原始 dpi,gSize 的值就和圖片的色彩格式有關(guān),具體對(duì)應(yīng)關(guān)系如下:
| 色彩格式 | 字節(jié)數(shù) |
|---|---|
| Alpha_8 | 1 |
| RGB_565 | 2 |
| ARGB_4444 | 2 |
| RGBA_8888 | 4 |
| BGRA_8888 | 4 |
| kIndex_8 | 1 |
| Unknown | 0 |
屏幕密度對(duì)應(yīng)關(guān)系:
| dpi | 比例 | |
|---|---|---|
| ldpi | 120 | 0.75 |
| mdpi | 160 | 1 |
| hdpi | 240 | 1.5 |
| xhdpi | 320 | 2.0 |
| xxhdpi | 480 | 3.0 |
| xxxhdpi | 640 | 4.0 |
上面的公式很好理解,如果要要在一個(gè) xxxhdpi 的設(shè)備上加載一個(gè)放在 xxhdpi 中的大小為 768 * 960 的 png 圖片時(shí)內(nèi)存大小就是
int(768 * 640/480 + 0.5f)* int(960 * 640/480 + 0.5f) * 4
網(wǎng)絡(luò)圖片大小
上面講了本地圖片加載到內(nèi)存中以后所占空間的計(jì)算方法,那如果是網(wǎng)絡(luò)圖片呢,由于它沒(méi)有原始 dpi,所以默認(rèn)的和當(dāng)前設(shè)備 dpi 一致,因此如果未做處理的情況下一個(gè)圖片的大小應(yīng)該為:
width * height * gsize
小結(jié)
由上面一通分析可以得知一個(gè) Bitmap 在內(nèi)存中的大小主要由以下決定:
- 圖片的顏色格式
- 圖片本身的大小
- 本地圖片所在的目錄
- 目標(biāo)設(shè)備的屏幕像素密度
參考文章:
理解 Bitmap