Android 内存优化4 - 图片优化

一、说明

图片占用内存的处理在整个应用中是非常重要的,特别是 Dalvik 版本虚拟机在 GC 后不能压缩内存。也就是说,当对象被释放时,留下的空间不是连续的。如果需要显示一张更大的图片,即使剩余内存比图片占用内存大,但由于其不连续性,仍然会导致 GC。

二、图片占用空间计算

一张图片占用的内存(BitMap) = width × height × 单位像素占用的字节数

在Android默认情况下,当图片文件解码成位图时,会被处理成32bit/像素。
红色、绿色、蓝色和透明通道各8bit,即使是没有透明通道的图片,如 jpeg 格式是没有透明通道的,但然后会处理成32bit位图,这样分配的32bit中的8bit透明通道数据是没有任何用处的,这完全没有必要,并且在这些图片被屏幕渲染之前,它们首先要被作为纹理传送到 GPU,这意味着每一张图片会同时占用 CPU 内存和 GPU 内存。

示例:一张 400 * 400 尺寸的图片,它所占用的空间计算为:
32bit * 400 * 400 = 625KB

三、图片内存优化

1、设置位图规格

Android提供了多种位图格式,根据开发者或产品需求,可以选择不同的格式解码图片:

最高的是RGB_8888,也就是系统默认的位图格式,其他几种都减小了位图通道位,可以减少内存开销并提升图片显示的性能。当然,这种空间的节省是要付出视觉质量受损的代价的,比如从RGB_8888改成使用RGB_565,会损失较多的图片数据,但不是不能用,根据不同场景可以选择不同的规格。

格式每像素占用空间说明应用场景
ARGB_888832Android 默认图片质量要求较高
RGB_56516除了大图模式,一般都可以使用,并且几乎看不出差别1.显示局部图片,比如列表中的图片 2.图片质量要求不高的场景 3.不需要透明通道
ARGB_4444161.需要更小的格式,但又需要透明通道 2.视觉差异比较大,一般用于用户头像,特别是圆角
ALPHA_881.主要用于Alpha通道模板,相当于作一个染色。 2.图像要渲染2次,虽然减少内存,但增加了绘制的开销。

2、inSampleSize

如果内存中的图片大于屏幕显示出的图片大小,或者大于指定屏幕区域的大小,这些高分辨率图片会导致严重的性能问题。因此,需要改变内存的占用,避免此类问题。
这里的问题在于,占据内存中实际未使用的区块,这些图片占用了内存堆中的大量空间,使应用空间变少,然后重置这些图片大小,让它们符合实际显示的大小,既能减小内存的开销,也能提高显示的效率,这样载入内存的图片规格符合实际显示规格,而不是完整的分辨率。

位图功能对象中的inSampleSize属性实现了位图的缩放功能,代码如下:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
BitmapFactory.decodeStream(is, null, options);

将这个属性设置为1时,可以在不加载完整大小图片的前提下,生成一张只有原始图片部分大小的新图片,如inSampleSize为2时获得只有1/2大小的图片,同理设置为4就是1/4大小的图片。因此,图片大小总会比原始图片小一倍以上。

3、inScaled、inDensity、inTargetDensity

虽然 inSampleSize 可以实现图片的缩放,都是指数幂的缩放,如果想更细地缩放图片,就需要使用位图的 inScaled、inDensity 和 inTargetDensity 功能。

options.inScaled = true;
options.inDensity = srcWidth;
options.inTargetDensity = dstWidth;

当inScaled设置为true时,系统会按照现有的密度来划分目标密度,通过派生绽放数来应用到位图上,使用这个方法会重设图片大小,并对它应用一个新的过滤。

虽然这些方法都非常好用,并且减少图片显示需要的内存,但因为过多的算法,导致图片显示的过程需要更多的时间开销,如果图片很多的话,就影响到图片的显示效果。最好的方案是结合这两个方法,达到最佳的性能结合,首先使用 insamplsesie 处理图片,转换为接近目标的2次幂,然后用 inDensity 和 inTargetdensy 生成最终想要的准确大小,因为inSamplesize会减少像素的数量,而基于输出密码的需要对像素重新过滤。但获取资源图片的大小,需要设置位图对象的 inJustDecodeBounds 值为 true,然后继续解码图片文件,这样才能生成图片的宽高数据,并允许继续优化图片。

options.inJusDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
options.inScaled = true;
options.inDensity = options.outWidth;
options.inSampleSize = 4;
options.inTargetDensity = dstWidth * options.inSampleSize;
options.inJusDecodeBounds = false;
BitmapFactory.decodeStream(is, null, options);

4、inBitmap

Android 3.0(API Level 11)引进了BitmapFactory.Options.inBitmap字段,如果设置了该属性,那么当使用了带有该Options参数的decode方法加载内容时,decode方法会尝试重用一个已经存在的位图。这就意味着位图内存已经被重用了,从而改善了性能,并且没有内存的分配和释放过程。

注意:新申请的Bitmap与旧的Bitmap必须有相同的解码格式,并且在Android 4.4之前,只能重用相同大小的Bitmap的内存区域,而Android 4.4之后可以重用任何bitmap的内存区域。

5、利用开源库

有几个非常好的开源库,如 Picasso、Glide,提供了更多、更优秀的处理位图的方式,并且实现了异步加载图片。

四、总结

在 Android 单个进程分配有限的空间基础上,由于图片占用空间比较大,对图片的处理尤为重要。善用 Android 自带的优化方案,再结合一些优化的开源库,可以为内存的优化带来极大的帮助。

五、推荐

  1. Android 性能优化1 - 启动优化
  2. Android 性能优化2 - 绘制优化
  3. Android 性能优化3 - 解决内存泄露
  4. Android 内存优化4 - 图片优化
  5. Android 性能优化5 - 内存优化
  6. Android 性能优化6 - Hybrid 应用启动优化
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值