Android应用性能优化系列视图篇——隐藏在资源图片中的内存杀手

图片加载性能优化永远是Android领域中一个无法绕过的话题,经过数年的发展,涌现了很多成熟的图片加载开源库,比如Fresco、Picasso、UIL等等,使得图片加载不再是一个头疼的问题,并且大幅降低了OOM发生的概率。然而,在图片加载方面我们是否可以就此放松警惕了呢?

开源图片加载库能为我们解决绝大部分有关图片的问题,然而并不是所有!

首先,图片从来源上可以分成三大类:网络图片、手机图片、APK资源图片。网络图片和手机图片都在图片加载库功能的覆盖范围内,基本上不用开发者太操心,但是APK资源图片却不在此范围!

关于APK资源图片有3个特征:
1、资源图片基本都是在xml中引用 ,在Java中也是通过资源ID查找 。
2、资源图片一般不使用异步记载,不会出现loading图这些中间状态。
3、资源图片不会加载失败,如果失败了那么APP也挂掉了。

正是由于这3点特征,所以图片加载库实在鞭长莫及。那么就很容易出现一个问题:图片过大导致OOM!

很多APP为了追求酷炫的效果,热衷于设计绚丽全屏背景页面。既然是为了炫酷,考虑到用户体验,这些全屏背景图自然不能使用网络图片了,所以,这些图片都被放在apk包中作为资源文件直接引用。

使用这些资源图片的方式一般都是:

android:background="@drawable/xxx"

正常情况下,这样使用自然不会出现问题,但是如果APP内存紧张,很容易就出现OOM,尤其是5.0版本以下的手机,经常跑着跑着就Crash了,始作俑者就是这个。

为了解决这种问题,最常用的方式是找设计师压缩图片。而压缩图片有两种方式:缩小尺寸和降低质量。那么,这两种方式是否有效呢?

1、缩小尺寸: 压缩图片的宽度和高度。由于图片的内存占用与宽高成正比,这种方式确实有效,但是图片显示时会被拉伸导致变形,从而失却美感。

2、降低质量: 降低图片的色彩度,像素颜色密度。这其实是一个误区,很多人认为图片的存储占用空间小,图片的内存占用就会小,其实是错误的观点。这是方式并不会影响图片的内存占用,反而由于质量降低(下文具体分析),使得页面缺乏质感。必须记住:图片的内存占用与图片质量毫无干系!

为了寻求一个合理的解决方案,必须知彼知己。下面,我们来详细分析下资源图片的内存占用的情况!(后文所说的图片,除非特殊指明,否则都默认指APK资源图片)。


1、计算Bitmap的内存占用

我们以一张标准720p的全屏图片为例,宽高比为720x1280,对应的资源文件夹为drawable-xhdpi。同样,设备以标准720p的小米2S手机为例,density=320。

首先,android设备上图片都被处理成Bitmap对象。生成Bitmap有一个非常重要的参数Config,属性值有ALPHA_8、RGB_565、ARGB_4444、ARGB_8888四种。不同的属性值对应的图片每个像素点占用内存大小不同,ALPHA_8每个像素占用1byte,RGB_565和ARGB_4444占用2byte,ARGB_8888占用4byte,其中ARGB_4444在高版本中已经废弃。

那么,资源图片被decode成Bitmap的时候,Config参数值是哪个呢?来看几段代码。

Resources.java

private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
   ...
   final String file = value.string.toString();
   ...
   final Drawable dr;
   if (file.endsWith(".xml")) {
       final XmlResourceParser rp = loadXmlResourceParser(file, id, value.assetCookie, "drawable");
       dr = Drawable.createFromXml(this, rp, theme);
       rp.close();
   } else {
       final InputStream is = mAssets.openNonAsset(value.assetCookie, file, AssetManager.ACCESS_STREAMING);
        dr = Drawable.createFromResourceStream(this, value, is, file, null);
        is.close();
   }
   ...
}

Drawable.java

public static Drawable createFromResourceStream(Resources res, TypedValue value,
            InputStream is, String srcName, BitmapFactory.Options opts) {
    ...
    if (opts == null) opts = new BitmapFactory.Options();
    opts.inScreenDensity = res != null ? res.getDisplayMetrics().noncompatDensityDpi : DisplayMetrics.DENSITY_DEVICE;
    Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
    ...
    return null;
}

BitmapFactory.java

public static class Options {
    ...

    /**
     * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by default.
     */
    public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;

    ...<
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值