在之前讲内存占用的博客中 【Android 内存优化】Bitmap 内存占用计算 ( Bitmap 图片内存占用分析 | Bitmap 内存占用计算 | Bitmap 不同像素密度间的转换 ) , 讲到从不同的像素密度资源中获取图片 , 其解码后的大小不同 ;
在上述博客最后从不同像素密度 , 加载 1990 x 1020 大小的图片 , 解码出来分别是如下结果 :
hdpi : 宽 3483 , 高 1785 , 占用内存 24868620 字节 ;mdpi : 宽 5224 , 高 2678 , 占用内存 55959488 字节 ; 从 drawable 默认目录中读取也是这个配置 ;xhdpi : 宽 2612 , 高 1339 , 占用内存 13989872 字节 ;xxhdpi : 宽 1741 , 高 893 , 占用内存 6218852 字节 ;xxxhdpi : 宽 1306 , 高 669 , 占用内存 3494856 字节 ;详细的计算过程查看上述博客 , 这里不再详述 ;
Bitmap 解码尺寸计算公式如下 :
加 载 到 内 存 中 的 宽 或 高 像 素 值 = 实 际 宽 或 高 像 素 值 × 本 机 像 素 密 度 图 片 存 放 的 目 录 对 应 的 像 素 密 度 加载到内存中的宽或高像素值 = 实际宽或高像素值 \times \dfrac{本机像素密度}{图片存放的目录对应的像素密度} 加载到内存中的宽或高像素值=实际宽或高像素值×图片存放的目录对应的像素密度本机像素密度
目前 R.drawable.blog 图片在 drawable 目录中存放 , 其代表的像素密度前缀是 mdpi ;
从该 drawable 目录中读取的资源 densityDpi 值为 DENSITY_MEDIUM = 160, 当前的 Pixel 2 手机屏幕密度 density = 2.625 , 屏幕像素密度 densityDpi = 420 ;
在博客 【Android 内存优化】Bitmap 图像尺寸缩小 ( 设置 Options 参数 | inJustDecodeBounds | inSampleSize | 工具类实现 ) 中出现如下问题 :
明明在代码中设置了宽高最大值时 100 x 100 , 解码出来的图片居然是 , 程序解析错了 ?
Bitmap reduceSizeBitmap = BitmapSizeReduce.getResizedBitmap(this, R.drawable.blog, 100, 100 , false , null);解码结果 : 解码出来的宽度 163 像素 , 高度 81 像素 , 明显出现问题了 ;
2020-06-30 22:04:22.959 3766-3766/? I/Bitmap: blog : 5224 , 2678 , 55959488 2020-06-30 22:04:22.960 3766-3766/? W/BitmapSizeReduce: getResizedBitmap inSampleSize=32 2020-06-30 22:04:22.980 3766-3766/? I/Bitmap: reduceSizeBitmap : 163 , 81 , 26406原因说明 :
在设置了 options.inJustDecodeBounds = true 选项后 , 使用 BitmapFactory.decodeResource(resources, iamgeResId, options) 解码出的图片参数 , 是图片的实际参数 , 即 1990 x 1020 , 此时按照该实际参数进行了图片解码 , 计算图片缩小值 inSampleSize = 32 , 此时是可以将图片宽高都缩小到 100 的 , 缩小后的图片宽高是 62 x 32 ;
如果从真实的图像解码 , 会将像素密度解码考虑进去 , 这里从 mdpi 资源中解码图片 , 实际的解码出来的大小是 5224 x 2678 , 如果将该值缩小 32 倍 , 肯定无法到达宽高都小于 100 像素 , 这里得到的图片大小事 163 x 81 ;
仔细阅读 DisplayMetrics 中的代码 , 可以看到不同像素密度的手机的资源来源 , 基本上是获取其向上取整屏幕密度的资源 , 如果当前手机 densityDpi = 420 , 其处于 DENSITY_XHIGH 与 DENSITY_XXHIGH 之间 , 那么就会优先读取 DENSITY_XXHIGH 对应 xxhdpi 中的资源 , 这也是为了保证图片清晰度设定的策略 ;
规则 : 当手机的屏幕像素密度处于两个标准量化值之间 , 那么会自动选取高的标准量化值对应的资源缩小后使用 ;
public class DisplayMetrics { // 低密度屏幕标准量化值 , 对应 ldpi , 现在基本不使用 public static final int DENSITY_LOW = 120; // DENSITY_LOW 与 DENSITY_MEDIUM 之间的密度 // 应用程序中不用考虑为这些像素密度准备资源 // 该密度的手机由系统自动缩放 DENSITY_MEDIUM 对应的资源使用 public static final int DENSITY_140 = 140; // 中等密度屏幕标准量化值 , 对应 mdpi public static final int DENSITY_MEDIUM = 160; // DENSITY_MEDIUM 与 DENSITY_HIGH 之间的密度 // 应用程序中不用考虑为这些像素密度准备资源 // 该密度的手机由系统自动缩放 DENSITY_HIGH 对应的资源使用 public static final int DENSITY_180 = 180; public static final int DENSITY_200 = 200; public static final int DENSITY_TV = 213; public static final int DENSITY_220 = 220; // 高密度屏幕标准量化值 , 对应 hdpi public static final int DENSITY_HIGH = 240; // DENSITY_HIGH 与 DENSITY_XHIGH 之间的密度 // 应用程序中不用考虑为这些像素密度准备资源 // 该密度的手机由系统自动缩放 DENSITY_XHIGH 对应的资源使用 public static final int DENSITY_260 = 260; public static final int DENSITY_280 = 280; public static final int DENSITY_300 = 300; // 超高密度屏幕标准量化值 , 对应 xhdpi public static final int DENSITY_XHIGH = 320; // DENSITY_XHIGH 与 DENSITY_XXHIGH 之间的密度 // 应用程序中不用考虑为这些像素密度准备资源 // 该密度的手机由系统自动缩放 DENSITY_XXHIGH 对应的资源使用 public static final int DENSITY_340 = 340; public static final int DENSITY_360 = 360; public static final int DENSITY_400 = 400; public static final int DENSITY_420 = 420; public static final int DENSITY_440 = 440; public static final int DENSITY_450 = 450; // 超超高密度屏幕标准量化值 , 对应 xxhdpi public static final int DENSITY_XXHIGH = 480; // DENSITY_XXHIGH 与 DENSITY_XXXHIGH 之间的密度 // 应用程序中不用考虑为这些像素密度准备资源 // 该密度的手机由系统自动缩放 DENSITY_XXXHIGH 对应的资源使用 public static final int DENSITY_560 = 560; public static final int DENSITY_600 = 600; // 超超超高密度屏幕标准量化值 , 对应 xxxhdpi public static final int DENSITY_XXXHIGH = 640; public static final int DENSITY_DEFAULT = DENSITY_MEDIUM; public static final float DENSITY_DEFAULT_SCALE = 1.0f / DENSITY_DEFAULT; public static int DENSITY_DEVICE = getDeviceDensity(); public static final int DENSITY_DEVICE_STABLE = getDeviceDensity(); // 省略一万行代码 ... }在 Bitmap 操作过程中 , 需要设置一系列与像素密度相关的取值 , 如 inDensity , inTargetDensity , setDensity 等值 ;
这些值设置的是 densityDpi 值 , 定义在 DisplayMetrics 中 ;
就是上述的 DENSITY_LOW 到 DENSITY_XXXHIGH 之间的一系列常量值 , 取值范围 120 ~ 640 ;
这两个值都是 BitmapFactory.Options 中设置的值 ;
① inDensity 像素密度值 : 设置该值会导致被返回的图像会被强制设置一个像素密度值 , 相当于设置了图片来自于哪个像素密度的资源 ;
② inTargetDensity 目标像素密度值 : 表示要缩放到的目标图像像素密度值 , 该值需要结合 inScaled 值使用 , 如果同时设置了 inScaled = true , 和 inDensity 像素密度值 , 在图像返回时 , 会自动将图像按照 inDensity 向 inTargetDensity 缩放 ;
// 设置图片的来源方向的像素密度 , 如设置 options.inDensity = decodeDensityDpi; // 设置图片的目标方向的像素密度 options.inTargetDensity = decodeDensityDpi; // 设置图片解码可缩放 , 该配置与上述两个配置结合使用 options.inScaled = true;执行结果 :
2020-07-01 11:17:31.389 12350-12350/kim.hsl.bm I/Bitmap: blog : 5224 , 2678 , 55959488 2020-07-01 11:17:31.390 12350-12350/kim.hsl.bm W/BitmapSizeReduce: getResizedBitmap options.outWidth=1990 , options.outHeight=1020 2020-07-01 11:17:31.390 12350-12350/kim.hsl.bm W/BitmapSizeReduce: getResizedBitmap inSampleSize=32 2020-07-01 11:17:31.413 12350-12350/kim.hsl.bm I/Bitmap: reduceSizeBitmap : 62 , 31 , 3844顺利将图片的宽高都缩小为 100 像素以下 ;
BitmapMemory