Android Bitmap

一、图片内存的占用

Bitmap作为位图,需要读入一张图片每一个像素点的数据,其主要占用内存的地方也正是这些像素数据。对于像素数据总大小,我们可以猜想为:像素总数量 × 每个像素的字节大小,而像素总数量在矩形屏幕表现下,应该是:横向像素数量 × 纵向像素数量,结合得到:
Bitmap内存占用 ≈ 像素数据总大小 = 横向像素数量 × 纵向像素数量 × 每个像素的字节大小
所以通过公式,我们可以看到图片的内存占用,跟像素的数量和每个像素占用字节大小有关

1.1 每像素占用字节大小

单个像素的字节大小由Bitmap的一个可配置的参数Config来决定。
Bitmap中,存在一个枚举类Config,定义了Android中支持的Bitmap配置:

Config 占用字节大小(byte) 说明
ALPHA_8 (1) 1 单透明通道
RGB_565 (3) 2 简易RGB色调
ARGB_8888 4 24位真彩色(这是Bitmap构建时的默认配置)
RGBA_F16 8 Android 8.0 新增(更丰富的色彩表现HDR)
HARDWARE Special Android 8.0 新增 (Bitmap直接存储在graphic memory)

我们根据具体的业务需求,给图片指定config格式:

bitmap.setConfig();

1.2 像素数量

图片的decode过程在JNI中的逻辑大致如下:

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 = decoded->width();
int scaledHeight = decoded->height();

if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
    scaledWidth = int(scaledWidth * scale + 0.5f);
    scaledHeight = int(scaledHeight * scale + 0.5f);
}
...
if (willScale) {
    const float sx = scaledWidth / float(decoded->width());
    const float sy = scaledHeight / float(decoded->height());
    bitmap->setConfig(decoded->getConfig(), scaledWidth, scaledHeight);
    bitmap->allocPixels(&javaAllocator, NULL);
    bitmap->eraseColor(0);
    SkPaint paint;
    paint.setFilterBitmap(true);
    SkCanvas canvas(*bitmap);
    canvas.scale(sx, sy);
    canvas.drawBitmap(*decoded, 0.0f, 0.0f, &paint);
}

一张图片的像素数量遵循这个公式:宽 * 高 * (targetDensity/density)
图片的宽高我们是知道的,targetDensity和density两个参数都是BitmapFactory的Options配置出来的。


image.png
  1. inTargetDensity和inScreenDensity一般都是设备决定
  2. inDensity(Bitmap位图自身的密度、分辨率):跟我们把图片放在哪个文件夹有关系,同一张图片放在不同的目录下,它的密度是不一样的
density 0.75 1 1.5 2 3 3.5 4
densityDpi 120 160 240 320 480 560 640
DpiFolder ldpi mdpi hdpi xhdpi xxhdpi xxxhdpi xxxxhdpi
  • 图片目标不匹配任何dpi的时候,默认是mdpi
  • 图片分辨率越高,解析后的图片越小,内存占用越小

二、图片的内存优化

知道了图片加载机制后,优化也就有了抓手:

  • 使用低色彩的解析模式
  • 尽可能的使用高分辨率的图片目录
  • 图片缩小,减少尺寸
    前两种方式需要根据图片的实际情况,选择合理的配置
    第三种方式需要对图片尺寸做裁剪:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = true; //只测量边框
BitmapFactory.decodeResource(getResources(), resId,options);
options.inJustDecodeBounds = false;
options.inSampleSize = BitmapUtil.computeSampleSize(options, -1, imageView.getWidth() * imageView.getHeight()); // 比较bitmap尺寸和目标尺寸,计算出缩放倍数
Bitmap newBitmap = BitmapFactory.decodeResource(getResources(), resId, options); //生成缩放后的bitmap
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容