Android加载大图的优化对策
Android加载大图的优化策略
当我们使用大的Bitmap图片时很容易出现OOM的现象,今天我们就来看下该怎么解决这个问题。
一般有两种方法:
1、压缩图片;
2、LruCache缓存;
当然这两种方式同时使用效果更好^^
一、压缩图片
先介绍下图片质量(Bitmap.Config),一共有4种:
ALPHA_8 只有透明度,没有颜色,那么一个像素点占8位。
RGB_565 即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位。
ARGB_4444 由4个4位组成,即A=4,R=4,G=4,B=4,那么一个像素点占16位。
ARGB_8888 由4个8位组成,即A=8,R=8,G=8,B=8,那么一个像素点占32位。
默认是ARGB_8888。
代码如下:
当我们使用大的Bitmap图片时很容易出现OOM的现象,今天我们就来看下该怎么解决这个问题。
一般有两种方法:
1、压缩图片;
2、LruCache缓存;
当然这两种方式同时使用效果更好^^
一、压缩图片
先介绍下图片质量(Bitmap.Config),一共有4种:
ALPHA_8 只有透明度,没有颜色,那么一个像素点占8位。
RGB_565 即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位。
ARGB_4444 由4个4位组成,即A=4,R=4,G=4,B=4,那么一个像素点占16位。
ARGB_8888 由4个8位组成,即A=8,R=8,G=8,B=8,那么一个像素点占32位。
默认是ARGB_8888。
据此我们可以写出如下函数:
//根据图片质量确定每个像素点所占字节数 public static int getBytesPerPixel(Bitmap.Config config) { if (config == Bitmap.Config.ARGB_8888) { return 4; } else if (config == Bitmap.Config.RGB_565) { return 2; } else if (config == Bitmap.Config.ARGB_4444) { return 2; } else if (config == Bitmap.Config.ALPHA_8) { return 1; } return 1; }
//根据Bitmap的宽高来计算其大小,知道图片大小后,我们可以决定是否对其压缩 public static long getBitmapSizeInMemory(int imageW, int imageH) { return imageH * imageW * getBytesPerPixel(Bitmap.Config.ARGB_8888); }BitmapFactory.Options有个inJustDecodeBounds属性,将inJustDecodeBounds设置为true时,就不解码图片到内存,只读取图片的基本信息,读取并设置之后,再把该值改为false,然后再进行解码获取图片,这就是压缩图片的原理。
代码如下:
//获取的inSampleSize必须是2的倍数 ps:本函数(getScaleInSampleSize)只是一种参考,具体实现还需要根据实际情况有所变动 public static int getScaleInSampleSize(int resW, int resH, int desW, int desH) { int scaleW = resW / desW; int scaleH = resH / desH; int largeScale = scaleH > scaleW ? scaleH : scaleW; int sampleSize = 1; while (sampleSize < largeScale) { sampleSize *= 2; } return sampleSize; } //获取压缩图片 public static Bitmap decodeBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // 第一次解析将inJustDecodeBounds设置为true,来获取图片信息 final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); int height = options.outHeight; int width = options.outWidth; String mimeType = options.outMimeType; // 调用上面定义的方法计算inSampleSize值 options.inSampleSize = getScaleInSampleSize(width, height, reqWidth, reqHeight); //options.inPreferredConfig= Bitmap.Config.RGB_565; //如有必要,还可以修改图片质量,进一步减小图片大小 // 使用获取到的inSampleSize值再次解析图片 options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }二、使用LruCache缓存
LruCache缓存主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值即将达到预设定值之前从内存中移除。
用法如下:
private LruCache<String, Bitmap> mMemoryCache; @Override protected void onCreate(Bundle savedInstanceState) { // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。 // LruCache通过构造函数传入缓存值,以KB为单位。 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // 使用最大可用内存值的1/6作为缓存的大小。 int cacheSize = maxMemory / 6; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // 重写此方法来衡量每张图片的大小,默认返回图片数量。 return bitmap.getByteCount() / 1024; } }; } public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } public Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } public void loadBitmap(int resId, ImageView imageView) { final String imageKey = String.valueOf(resId); final Bitmap bitmap = getBitmapFromMemCache(imageKey); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { //如果缓存里面没有就使用默认的图片 imageView.setImageResource(R.drawable.image_default); LoadWorkerTask task = new LoadWorkerTask(); task.execute(resId); } } class LoadWorkerTask extends AsyncTask<Integer, Void, Bitmap> { // 在后台加载图片。 @Override protected Bitmap doInBackground(Integer... params) { final Bitmap bitmap = decodeBitmapFromResource( getResources(), params[0], 100, 100); addBitmapToMemoryCache(String.valueOf(params[0]), bitmap); return bitmap; } }
代码已经很清楚了,就不解释了。
只要我们掌握了这两种方法,那么当我们需要使用大尺寸或是使用多张Bitmap时就不需要再担心OOM问题了。