由于Bitmap的特殊性以及Android对单个应用所规定的最大内存限制,我们在同时加载大量Bitmap时很容易发生内存溢出,即我们通常所说的OutOfMemoryError(OOM),因此高效加载Bitmap就成为了每个Android开发者的必备技能。

在学习如何高效地加载Bitmap之前,首先介绍一下如何加载一个Bitmap。我们都知道,Bitmap在Android中通常指的是一张图片,那么如何将JPG、PNG等格式的图片转换成Bitmap对象呢?BitmapFactory类给我们提供了一些方法:

  • public static Bitmap decodeFile(String pathName, Options opts) //从文件读取图片
    public static Bitmap decodeFile(String pathName)

  • public static Bitmap decodeStream(InputStream is) //从输入流读取图片
    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)

  • public static Bitmap decodeResource(Resources res, int id) //从资源文件读取图片
    public static Bitmap decodeResource(Resources res, int id, Options opts)
    decodeResource是在 java 层完成缩放的,效率比较低,而且需要消耗 java 层的内存

  • public static Bitmap decodeByteArray(byte[] data, int offset, int length) //从数组读取图片
    public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)

  • public static Bitmap decodeFileDescriptor(FileDescriptor fd)//从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高
    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

接下来开始介绍如何高效地加载Bitmap,其实核心思想很简单:就是采用BitmapFactory.Options参数来调整图片尺寸来适配控件的大小。

假如我们显示图片的控件ImageView宽高为100×100像素,而图片的尺寸为1024×1024像素,这个时候如果将整个图片加载进来并显示到控件上,自然是很占用内存资源的。这个时候可以通过BitmapFactory.Options按一定的采样率加载缩小后的图片,再将缩小后的图片显示到ImageView中,这样就能减小内存占用从而在一定程度上避免OOM的发生。

通过BitmapFactory.Options来缩放图片,主要是使用它的inSampleSize参数,也就是前面提到的采样率。当采样率inSampleSize为1时,采样后的图片大小为原图大小;当采样率inSampleSize>1,比如为2时,采样后的图片宽高都为原图的1/2,即像素降为原图的1/4,占用的内存大小也就是原图的1/4;比较特殊的是,当采样率inSampleSize<1时,系统会自动将该值当做1来处理。因此可以得出一个结论:采样率inSampleSize必须是大于1的整数图片才会有缩小的效果,并且采样率同时作用于宽高,也就是说采样后的图片会缩小到原图的1/(inSampleSize^2)。比如inSampleSize=4,那么缩放比例为1/16。

我们现在知道了,通过采样率可以提高图片的加载效率,那么如何才能计算出最合适的采样率?我们可以按照如下流程:

  1. 将BitmapFactory.Options的inJustDecodeBounds参数设置为true并加载图片。
  2. 从BitmapFactory.Options中取出图片的原始宽高,对应于outWidth和outHeight参数。
  3. 根据目标View所需大小计算出采样率inSampleSize。
  4. 将BitmapFactory.Options的inJustDecodeBounds参数设置为false,然后重新加载图片。

inJustDecodeBounds参数需要说明一下,当inJustDecodeBounds设为true的时候,BitmapFactory只会解析出图片的原始宽高信息,并不会真正地加载图片,所以这个操作是轻量级的。

接下来以decodeFile方法为例实现图片的缩放,其他三个方法处理方式类似。

/** * 高效加载Bitmap * * @param filePath      文件路径 * @param requestWidth  所需宽度 * @param requestHeight 所需高度 */public static Bitmap decodeSampleBitmap(String filePath, int requestWidth, int requestHeight) {    BitmapFactory.Options options = new BitmapFactory.Options();    //第一次解析图片原始宽高信息,不会真正去加载图片    options.inJustDecodeBounds = true;    BitmapFactory.decodeFile(filePath, options);    //计算采样率inSampleSize    options.inSampleSize = calculateInSampleSize(options, requestWidth, requestHeight);    //第二次解析图片,真正加载图片    options.inJustDecodeBounds = false;    return BitmapFactory.decodeFile(filePath, options);}/** * 计算采样率 * * @param options   图片信息 * @param reqWidth  所需宽度 * @param reqHeight 所需高度 * @return 采样率 */public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {    final int width = options.outWidth;    final int height = options.outHeight;    int inSampleSize = 1;    if (height > reqHeight || width > reqWidth) {        //计算图片原始宽高和所需宽高比例值        final int heightRatio = Math.round((float) height / (float) reqHeight);        final int widthRatio = Math.round((float) width / (float) reqWidth);        //取宽高比例值中的较大值作为inSampleSize        inSampleSize = heightRatio > widthRatio ? heightRatio : widthRatio;    }    return inSampleSize;}

下一篇:Android 图片加载(二)图片加载框架Glide 入门篇

参考

《Android开发艺术探索》

更多相关文章

  1. Android(安卓)matrix 控制图片的旋转、缩放、移动
  2. android拍照与读取相册
  3. Android(安卓)报错:Caused by: android.os.FileUriExposedExcepti
  4. Android(安卓)P SystemUI之StatusBar UI布局status_bar.xml解析
  5. Android--SoLoader,android动态加载so库
  6. Android异步加载图像小结 (含线程池,缓存方法)
  7. android解决坚屏拍照和保存图片旋转90度的问题,并兼容4.0
  8. Android(安卓)Paging组件Demo
  9. Android(安卓)TabHost使用、动态加载内容

随机推荐

  1. python 读写json数据
  2. 在Python中解析Yaml:检测重复的密钥
  3. 批量重命名文件——python实现
  4. 用15行perl打造win32下的简易后门 python
  5. python使用多进程爬取图片
  6. Python 3.4 AssertEqual()在Django单元测
  7. pandas - 将嵌套字典值映射到dataframe
  8. 【python coding 1:网络检测】ping本地文
  9. 在Python中搜索一个并行数组
  10. python的requests类库(一)requests库和urll