Android Bitmap大量使用不产生OOM之“加载大图片资源优化”

转载请注明 http://blog.csdn.net/sinat_30276961/article/details/47303539

几乎所有Android应用都会涉及到图片,而Android图片的展示是通过Bitmap的。我们开发过应用的都或多或少了解到Bitmap的使用有诸多麻烦,使用不当就是OOM,即OutofMemoryError。那么怎么确保既能开心的使用大量的Bitmap,又不会出现OOM呢?
Google官方的doc给了我们很好的方案,我计划会把bitmap优化分为几篇进行讲解。

我们先来看看为啥要关注bitmap使用优化:
1.移动设备通常有限制的系统资源。比方说,Android设备可以限定每个应用只能最多分配16M。也就是说你的应用必须优化到占用内存少于16M。
2.Bitmap恰好是占用内存的大头。比方说,Galaxy Nexus拍张照像素最高可到 2592x1936。然后bitmap默认使用ARGB_8888去解析颜色。那么这张图就占用大概19M(2592*1936*4 bytes)。这样,立马就超了刚才举例的内存限制。
3.Android ui经常大量的使用bitmap。比方说,listview,gridview,viewpager。一般在页面上要展现很多bitmap。还包括未展现,但即将被滑到的那部分。

在很多种情况下,我们界面所要展现的图片,往往比实际的图片像素要求低。而且用这些高像素的图片放到低像素要求的界面上,也不会有显示效果上的优势。反倒会消耗大量内存,影响效率,因为,这个过程需要缩放。

读取bitmap的规模和类型

BitmapFactory提供一系列针对不同数据源的解析方法(decodeByteArray(), decodeFile(), decodeResource()等等)。这些方法在构造bitmap的时候会分配内存,所以,很容易造成OOM。每个解析方法都提供了一个可选的参数BitmapFactory.Options用来定义解析的属性。可以通过设置inJustDecodeBounds这个属性为true,来阻止解析时分配内存,此时解析返回null,但是赋值了options内部outWidth,outHeigth和outMimeType。这个技术允许你在不分配内存的情况下获得bitmap的尺寸和类型。看代码:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

除非你十分肯定你解析的图片很合适,不会超出内存使用范围,否则你还是得按照上面的方式来预解析下。

内存加载缩小版

根据上面的技术,已经得到图片规格。然后你可以通过这些尺寸,决定是否直接加载,还是加载缩小版。这里罗列一些考虑因素:
1.评估加载完整图片所需内存
2.为了加载这张图片你愿意提供多少内存
3.展示这张图的控件(Imageview或其他)的尺寸大小
4.当前屏幕的尺寸和屏幕像素密度
比方说实际展示的控件尺寸只有128x96px,你却去解析一个1024x768 px的图片给它,这样是不划算的。

那么怎么解析成缩小版的图片,只要通过设置BitmapFactory.Options的inSampleSize的值就行。比方说,原先是2048x1536的,通过设置inSampleSize值为4,就会解析成512x384。这在ARGB_8888情况下,相当于从原先要加载12MB,变为加载0.75MB。下面给出通过目标要求的宽和高,来计算出inSampleSize的代码:

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

使用2的倍数增加是因为解析时使用的最终值大概是2的倍数,这样增加解析效率。

在使用这个方法前,先要通过设置inJustDecodeBounds为true,解析一次,获取到options的图片原始宽高。然后计算出inSampleSize之后,把inJustDecodeBounds置为false,再解析一次。看代码:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

使用上面的方法,可以很轻松的把大分辨率图片以100x100的分辩率加载到ImageView,如下代码所示:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

上述图片源是资源文件形式,其他图片源形式的解析过程和上述类似,只要使用正确的解析方法就行BitmapFactory.decode*

下一篇Android Bitmap大量使用不产生OOM之多线程并发加载Bitmap的处理方式,如果有兴趣,可以进去看看。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值