Android图片优化OOM

300 阅读2分钟

图片OOM问题的产生

解决

图片尺寸压缩

  1. BitmapFactory.Options的属性inJustDecodeBounds = true,即在不加载图片进内存的情况下获取图片的宽高。
  2. 计算合适的压缩比inSampleSize,设置inSamleSize >= 2 即可降低图片的采样率,减小图片的内存占用。
  3. 单纯改变ImageView的大小无用,因为图片存储在Bitmap中,需要优化Bitmap。

质量压缩

  • RGB_565一个像素占两个字节,ARGB_8888一个像素占4个字节。可在BitmapFactory.Options的属性inPreferredConfig中设置。

内存重用

  • 后面的图片通过inBitmap复用第一张图片所占用的内存,但后面的图片申请的内存大小需要小于或等于第一张图片占用的内存大小,Android 3.0后才可使用。

  • Android 3.0以后,像素数据和Bitmap(位图)都存在Dalvik堆中,如果没有对象引用该Bitmap,gc会自动回收这些数据,无需像3.0之前调用Bitmap.Recycle手动回收。

压缩图片示例

  • 把图片宽度压缩至200像素。
  • 用inBitmap复用内存,mCurrentBitmap是第一张图片的Bitmap对象。

图片加载优化

照片墙示例

  1. 计算照片的显示高度
  • realHeight/realWidth:图片的真实高度/宽度。 scaleHeight/scaleWidth:缩放后的高度/宽度。
  1. 图片多级缓存加载流程

超大图片加载方案

  • 超大图片指图片宽度/高度大于屏幕宽度/高度的图片。
  • 利用自定义View的设计思想,即是通过滑动(手势类监听GestureDetector.OnGestureListener)时获得的坐标(getRawX()/getRawY())不断设置矩形区域的偏移坐标,然后不断绘制View(invalidate()回调onDraw())来完成动态显示大图的效果。
  • BitmapRegionDecoder主要用于显示图片的某一块矩形区域,所以可以利用它来完成大图片的动态区域显示。
  • BitmapRegionDecoder.decodeRegion()方法,通过传入矩形区域即可显示图片的指定区域。矩形Rect区域(4个顶点)会通过手势获取到的x/y偏移量更新。
 Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect, options);

使用例子

        try {
            InputStream is = getAssets().open("qm.jpg");
            BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
            tmpOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, tmpOptions);
            // 获得图片宽高
            int width = tmpOptions.outWidth;
            int height = tmpOptions.outHeight;

            // 设置显示图片的中心区域
            BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder
                                             .newInstance(is, false);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            // RGB_565一个像素占两个字节,ARGB_8888一个像素占4个字节
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            // 设置显示区域的矩形大小
            Rect rect = new Rect(
                    width / 2 - 100, height / 2 - 100,
                    width / 2 + 100, height / 2 + 100);
            // 通过BitmapRegionDecoder来解析显示区域的图像
            Bitmap bitmap = bitmapRegionDecoder.decodeRegion(rect, options);
            mShow.setImageBitmap(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }

常见图片加载框架

  1. Picasso 详见 juejin.cn/post/685457…

  2. Glide 详见 juejin.cn/post/685041…

  3. Fresco

使用它的SimpleDraweeView。

图片优化总结