Android如何高效加载大图

来源:互联网 发布:linux删除一个目录 编辑:程序博客网 时间:2024/04/27 22:22

今天的学习目标是如何高效的加载大图
众所周知,android 在加载数量很多的大图的时候,容易引起OOM异常(内存溢出),这是为什么呢?是因为现在的手机图片的分辨率越来越高,图片越来越多,而系统给单个应用施加了内存限制,比如16MB,这就导致加载bitmap的时候,很容易就内存溢出了。

bitmap相关知识介绍

bitmap表示的是位图,也就是图片,获取bitmap的方法是什么呢?
BitmapFactory提供了

decodeStream(); decodeResource(); decodeFile(); decodeByteArray();

这样的四个方法,分别表示在输入流,资源文件,文件,和字节数组中加载一个bitmap对象。相信这些方法大家一定使用过。

高效加载图片的思路

那么怎么高效的加载一张图片呢?比如一张1920*1080的图片,仅仅只是放在一个128*96的imageview控件里面,如果直接把图片放到控件里面,那么系统还是会把整个图片加载到内存中,再赋给imageview,这样就显的很壕。所以我们要做的就是按比例缩小图片,只在内存中加载缩小后的图片,图片被缩小后当然就没有原始图片那么大了,对内存的占用量,也就没有那么大了。

加载图片的方法

那么问题来了。

  1. 什么时候缩小
  2. 怎么缩小

我们来一一解答这些问题:
我们在缩小图片前要做一个判断,判断这张图片是否应该缩小,判断依据是什么呢,首先,我们先获取图片的大小,再和imageview所设置的大小做对比,如果图片的宽和高都大于我们所规定的大小,这当然得缩小,那如果图片比规定大小只大那么一丢丢,应该怎么办呢,这个我们后面会讨论。
首先要解决的问题是怎么获取图片的大小,在BitmapFactory里面有一个内部类Options,该类的属性可以控制decodeResource()这些方法的行为。比如inJustDecodeBounds这个属性,这个属性为boolean类型的,当该值为true的时候,decodeResource()所返回的bitmap是null,但是可以获取到图片的outWidth, outHeight 与 outMimeType,所以:

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;

获取到图片的尺寸了,判断也就容易了,那么我们怎么缩小图片呢?
在Options里面有一个inSampleSize,是一个int型的值,假设我们现在有一张1000*1000的图片,如果inSampleSize设为2,表示长宽统统除以2,缩小后的图片便是500*500,面积就为原来的1/4,内存自然也就只有原来的1/4了,本来10MB的图片,现在瞬间变2.5MB了,如果inSampleSize设为4,那缩小后的图片就只有0.625MB,内存终于保住了有木有!!具体怎么做呢,额,我们还是先根据图片大小和规定大小比对一下,算出最适合的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;}

通过该方法,我们会发现,获取的数字是1、2、4、8、16之类的数字,都是以2为指数的幂,这是官方规定的。

既然已经获取到了inSampleSize,那么是时候获取图片了,先将Options的inJustDecodeBounds值设置为true,再将options传到decodeResource()里面,这样的options就可以获取到图片的大小了,然后计算出inSampleSize的大小,再将inJustDecodeBounds设置为false,真正的获取bitmap:

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);}

使用方法就显而易见了。

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

为了对比效果,我做了个小例子,例子很简单,布局里面一个128*96的imageview图片,然后把drawable里面的图片赋给imageview,结果效果惊人。

普通设置方法:

img.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.img));

内存使用量:
这里写图片描述

高效加载方法:

img.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.drawable.img,128,96));

内存使用量:
这里写图片描述

以上就是高效加载大图的方式,你学会了吗?

0 0