参考:
https://www.cnblogs.com/dasusu/p/9789389.html
http://www.cocoachina.com/articles/30884
计算公式:
1、如果设置了inSampleSize=1,2,4,8 , 则相应的
Sample高度 = 原图高度 * 1,1/2, 1/4, 1/8;
Sample宽度 = 原图宽度 * 1,1/2, 1/4, 1/8;
2、(BitmapFactory 中 decodeResource() 方法内部会根据 dpi 进行分辨率的转换,其他 decodeXXX() 就没有了,所以,如果不是decodeResource() 或 图片的来源,如磁盘(assets, raw),文件,流等, 则跳过这步)
新图的高度 = Sample高度 * (设备的 dpi / 目录对应的 dpi )
新图的宽度 = Sample宽度 * (设备的 dpi / 目录对应的 dpi )
3、
bitmap内存 = 新图的高度 * 新图的宽度 * 每像素大小
参数看下面:
每像素大小根据options中的:
Bitmap.Config.ARGB_8888 : 8bit + 8bit + 8bit + 8bit = 32bit = 4Byte
Bitmap.Config.ARGB_4444 : 4bit + 4bit + 4bit + 4bit = 16bit = 2Byte
Bitmap.Config.RGB_565 : 5bit + 6bit + 5bit = 16bit = 2Byte
原图宽高:
6个目录对应dip:
L DPI ( Low Density Screen,120 DPI ),0.75
M DPI ( Medium Density Screen, 160 DPI ),1
H DPI ( High Density Screen, 240 DPI ),1.5
XH DPI ( Extra-high density screen, 320 DPI ),2
XXH DPI( xx-high density screen, 480 DPI ),3
XXXH DPI( xxx-high density screen, 640 DPI ),4
@Override
public void draw(@NonNull Canvas canvas) {
try {
if (mResources != null && mParentViewHeight != 0 && mParentViewWidth != 0) {
if (mLastBitmap == null) {
mLastBitmap = Bitmap.createBitmap(mParentViewWidth, mParentViewHeight, Bitmap.Config.ARGB_4444);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(mResources, RES_IDS[curFrame], options);
options.inScaled = true;
options.inDensity = mInDensity;
options.inTargetDensity = mInTargetDensity;
options.inJustDecodeBounds = false;
options.inMutable = true;
options.inSampleSize = 4;
options.inBitmap = mLastBitmap; //bitmap内存复用
Bitmap bitmap = BitmapFactory.decodeResource(mResources, RES_IDS[curFrame], options);
int canvasWidth = canvas.getWidth();
int bitMapHeight = bitmap.getHeight();
int bitMapWidth = bitmap.getWidth();
canvas.drawBitmap(bitmap, bitMapWidth,bitMapHeight, mPaint);
Log.e("chen", "density : " + mResources.getDisplayMetrics().density);
Log.e("chen", "byteCount : " + bitmap.getByteCount() + " AllocationByteCount : " + bitmap.getAllocationByteCount());
}
bitmap.getByteCount() :bitmap使用内存的理论值。
bitmap.getAllocationByteCount() :使用options.inBitmap时,bitmap实际使用的内存。
内存优化:
1、设置inSampleSize
2、不影响用户体验的情况下:Bitmap.Config.ARGB_4444
举例:
1、
2、
在 Android 中,计算 Drawable
(尤其是 Bitmap
)的内存占用大小需要根据 图片的像素数据、色彩格式 以及 内存管理机制 进行计算。以下是详细的计算方法和关键因素:
1. Bitmap 内存占用计算公式
Bitmap 的内存大小主要由以下因素决定:
- 宽度(Width):像素宽度(px)
- 高度(Height):像素高度(px)
- 色彩格式(Color Format):每个像素占用的字节数
公式
内存大小 (Bytes) = 宽度 × 高度 × 每个像素的字节数
常见色彩格式的像素字节数
色彩格式(Bitmap.Config ) | 每个像素占用字节数 | 说明 |
---|---|---|
ALPHA_8 | 1 byte | 仅透明度(无颜色) |
RGB_565 | 2 bytes | 5位红 + 6位绿 + 5位蓝(无透明度) |
ARGB_4444 (已废弃) | 2 bytes | 4位 Alpha + 4位 R/G/B |
ARGB_8888 (默认) | 4 bytes | 8位 Alpha + 8位 R/G/B |
RGBA_F16 (Android 8.0+) | 8 bytes | 16位浮点色彩(广色域支持) |
示例计算
假设一张 1000×1000
的图片:
ARGB_8888
(默认):1000 × 1000 × 4 = 4,000,000 bytes ≈ 3.81 MB
RGB_565
(节省内存):1000 × 1000 × 2 = 2,000,000 bytes ≈ 1.91 MB
2. 考虑 Bitmap 的缩放(Density 和 Target Density)
Android 会根据 设备屏幕密度(dpi) 和 资源目录(drawable-xxhdpi 等) 自动缩放 Bitmap,影响最终内存占用。
缩放计算公式
实际内存大小 = (原始宽度 × 缩放比例) × (原始高度 × 缩放比例) × 每像素字节数
其中:
缩放比例 = 设备屏幕密度(dpi) / 资源目录密度(dpi)
常见资源目录密度
资源目录 | 密度(dpi) |
---|---|
drawable-ldpi | 120 |
drawable-mdpi | 160 |
drawable-hdpi | 240 |
drawable-xhdpi | 320 |
drawable-xxhdpi | 480 |
drawable-xxxhdpi | 640 |
示例
假设:
- 图片放在
drawable-xxhdpi
(480 dpi) - 设备屏幕密度为
320 dpi
(xhdpi) - 图片原始尺寸
1000×1000
,格式ARGB_8888
计算:
缩放比例 = 320 / 480 ≈ 0.6667
实际内存大小 = (1000 × 0.6667) × (1000 × 0.6667) × 4 ≈ 666 × 666 × 4 ≈ 1,777,824 bytes ≈ 1.7 MB
结论:比原始内存(4MB)小,因为系统自动缩小了 Bitmap。
3. 获取 Bitmap 内存占用的代码实现
方法 1:直接计算
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
long memorySize = bitmap.getByteCount(); // 直接返回内存大小(Bytes)
Log.d("BitmapMemory", "Memory: " + memorySize / (1024 * 1024) + " MB");
方法 2:手动计算(适用于 API < 12)
int bytesPerPixel;
switch (bitmap.getConfig()) {
case ALPHA_8:
bytesPerPixel = 1;
break;
case RGB_565:
case ARGB_4444:
bytesPerPixel = 2;
break;
case ARGB_8888:
default:
bytesPerPixel = 4;
break;
}
long memorySize = (long) bitmap.getWidth() * bitmap.getHeight() * bytesPerPixel;
4. 优化 Bitmap 内存占用
(1)使用 inSampleSize
进行压缩
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; // 缩小 2 倍(宽度/高度各减半)
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
计算方式:
内存大小 = (原始宽度 / inSampleSize) × (原始高度 / inSampleSize) × 每像素字节数
(2)使用 RGB_565
替代 ARGB_8888
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565; // 减少内存占用
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
(3)及时回收 Bitmap
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle(); // 释放 Native 内存
bitmap = null;
}
5. 总结
影响因素 | 说明 |
---|---|
宽度 × 高度 | 像素尺寸越大,内存占用越高 |
色彩格式 | ARGB_8888 (4B) > RGB_565 (2B) |
屏幕密度缩放 | 低密度设备加载高密度图片会缩小,减少内存 |
inSampleSize | 采样压缩可大幅降低内存 |
Bitmap 复用 | 使用 BitmapFactory.Options.inBitmap 复用内存 |
最终建议:
- 尽量使用
RGB_565
或inSampleSize
压缩大图。 - 使用
Glide
或Picasso
等库自动优化 Bitmap 内存管理。