由于 Bitmap 的特殊性和 Android 对于每一个应用所施加的内存限制,就会导致加载图片可能会出现 OOM 内存溢出,所有要如何高效加载一个图片是很重要的。
那么在 Android 如何加载一个图片呢?BitmapFactory 类提供了四种方法:decodeFile、decodeResource、decodeStream、decodeByteArray。从它们名字我们就可以看出,它们分别支持从文件系统、资源、输入流和字节数组中加载 Bitmap 对象,其中 decodeFile 和 decodeResource 又间接调用了 decodeStream。
那么如何高效加载图片呢?核心思想就是使用 BitmapFactory.Options 来加载所需尺寸的图片。例如当使用 ImageView 加载图片,当图片原始尺寸比 ImageView 大时,就需要使用 BitmapFactory.Options 将图片以一种比例缩小。这样加载缩小的图片展现到 ImageView 上,就可以降低内存在一定程度上避免 OOM。
通过 BitmapFactory.Options 来缩放图片,主要是利用了它的 inSampleSize 参数,即采样率。当 inSampleSize 为1时,图片为原图大小,当 inSampleSize = n(n>1) 时,图片高宽均为原来的 1/n,像素为原来的 1/(n*n),内存也为原来的 1/(n*n)。
如果,ImageView 和 Bitmap 高宽是相同的比率,那么直接缩放基本上没有问题,但是如果比率不同,图片缩放得稍小就会被拉伸导致模糊。
- 如何获取采样率
- 将 BitmapFactory.Options 的 inJustDecodeBounds(此参数为 true 时,BitmapFactory 只会解析图片的原始高宽,而不会加载,所以是一个轻量级操作)参数设为 true,并加载图片。
- 从 BitmapFactory.Options 中取出的原始高宽对应为 outWidth、outHeight。
- 利用采样率规则和 VIew 的所需大小计算采样率 inSampleSize 。
- 将 BitmapFactory.Options 的 inJustDecodeBounds 设为 false,重新加载图片。
public static Bitmap decodeSampleBitmapFromResources(Resources res,int resId ,int reqWidth,int reqHeight){ final BitmapFactory.Options options = new BitmapFactory.Options(); //设置 inJustDecodeBounds 参数为true,解析原图 options.inJustDecodeBounds = true; //加载原图 BitmapFactory.decodeResource(res,resId,options); //获取采样率 options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight); //设置 inJustDecodeBounds 参数为false,不解析原图 options.inJustDecodeBounds = false; //返回压缩后的图片 return BitmapFactory.decodeResource(res,resId,options); } public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){ //获取图片原始的宽高 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 = height/2; //直到压缩到比需要大小小 while ((halfHeight / inSampleSize)>= reqHeight && (halfWidth / inSampleSize)>= reqWidth){ inSampleSize*=2; } } return inSampleSize; }
当我们需要高效加载图片时,可以就直接调用 decodeSampleBitmapFromResources()方法。而BitmapFactory的其它三个方法也是试用的。
大家如果感兴趣可以看我下一篇 Android 缓存策略。
学习自《Android 开发艺术探索》