Android(安卓)大图压缩处理,避免OOM
16lz
2021-01-24
一、遇到问题: Android的开发的小伙伴在项目中有时遇到多张大图片的加载,图片的类型是多种多样,不同大小。但是大多数的图片都是高分辨率,总体来说,远远大于我们要展示的容器ImageView。有些就认为,图片的分辨率高和图片太大顶多就消耗更多流量而已,其实不是的,其实我们编程的运用程序是有内存限制的。程序的内存如果过高就会出现OOM异常。我们先在应用程序中编程以下代码,观察一下现象:
从这个打印信息,可以看出,我们的应用程序的内存是多么宝贵的!所以在我们展示图片的时候,对图片进行处理,处理一般包括两种:图片质量压缩、图片大小压缩。在不要求高分辨率的需求下,可以对图片进行质量压缩。至于图片大小压缩,可以根据ImageView控件的展示图片长宽,将图片压缩至相近的距离,不但不会影响显示的效果,而且有效的减少OOM。接下来我们看一看,怎么对一张大图片的处理呢?
二、单张大图片压缩(质量和比例) 查看api文档,Android图片Bitmap对象是用BitmapFactory工具类中多种解析方法创建的。 创建的方式大概分为: 1)字节数组解析方式
2)SD卡文件中加载 解析 方式
3)资源中文件加载 解析 方式
4)网络中加载流的解析方式
这些方法会尝试为已经构建的bitmap分配内存,这时就会很容易导致OOM出现。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。
//最大从虚拟机能挖过来的内存long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024;//挖过来的内存没用上称之为freeMemory,基本都是要多少挖它多少。long freeMemory = Runtime.getRuntime().freeMemory()/1024/1024;//已经从操纵系统那里挖过来的内存大小,也就是java虚拟机这个进程当时所占用的所有内存long totalMemory = Runtime.getRuntime().totalMemory()/1024/1024;
打印出: 从这个打印信息,可以看出,我们的应用程序的内存是多么宝贵的!所以在我们展示图片的时候,对图片进行处理,处理一般包括两种:图片质量压缩、图片大小压缩。在不要求高分辨率的需求下,可以对图片进行质量压缩。至于图片大小压缩,可以根据ImageView控件的展示图片长宽,将图片压缩至相近的距离,不但不会影响显示的效果,而且有效的减少OOM。接下来我们看一看,怎么对一张大图片的处理呢?
二、单张大图片压缩(质量和比例) 查看api文档,Android图片Bitmap对象是用BitmapFactory工具类中多种解析方法创建的。 创建的方式大概分为: 1)字节数组解析方式
2)SD卡文件中加载 解析 方式
3)资源中文件加载 解析 方式
4)网络中加载流的解析方式
这些方法会尝试为已经构建的bitmap分配内存,这时就会很容易导致OOM出现。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。
/** * 从SD卡中获取图片并且比例压缩 * @param path 路径 * @param mHeight 自定义高度 * @param mWidth 自定义宽度 * @return */public static Bitmap getBitmapFromSDCard(String path, int mHeight, int mWidth){ BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); //计算比例值 options.inSampleSize = calculateInSampleSize(options,mHeight,mWidth); options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(path, options);}/** * 计算压缩比例值inSampleSize * @param options 压缩的参数设置 * @param mHeight 想要的高度 * @param mWidth 想要的宽度 * @return */public static int calculateInSampleSize(BitmapFactory.Options options, int mHeight, int mWidth){ //原尺寸大小 int yHeight = options.outHeight; int yWidth = options.outWidth; int inSampleSize = 1; //如果宽度大的话根据宽度固定大小缩放 if (yWidth > yHeight && yWidth > mWidth) { inSampleSize = (int) (yWidth / mWidth); } //如果高度高的话根据宽度固定大小缩放 else if (yWidth < yHeight && yHeight > mHeight) { inSampleSize = (int) (yHeight / mHeight); } if (inSampleSize <= 0) inSampleSize = 1; return inSampleSize;}
观察代码,可以看出,先后解析了两次图片,首先options.inJustDecodeBounds = true以后,BitmapFactory解析一次图片,将图片的得到的option传入到方法calculateInSampleSize方法中,在方法中可以根据ImageView控件的长宽对图片进行压缩,计算出合适的inSamplesize值,最后 options.inJustDecodeBounds 设置 false以后,在对图片解析一次,这时的图片的比例值 inSamplesize正是我们计算出来的值。 调用getBitmapFromSDCard方法即可得到压缩过的Bitmap对象了。 //得到100*100图片mImageView.setImageBitmap(BitMapUtil.getBitmapFromSDCard(path,100,100));
上面的这样做已经可以图片的比例压缩,至于有需要对图片质量压缩的可以参照一下代码。 /** * 图片质量压缩(质量参数) * @param image * @param quality 质量参数 百分之多少 例如:0.6 0.7 * @return */public static Bitmap compressImage(Bitmap image, float quality){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, (int)quality*100, baos); ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中 return BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片}
掌握了以上方法,不管是要在程序中加载超大图片,还是要加载大量图片,都不用担心OOM的问题了!如果有出错或者需要改进的地方,欢迎指点或者交流。 更多相关文章
- 第九章:Android中的数据存取
- android动态增加控件时控制样式的方法
- Android(安卓)滑动绘制流程探究 系统是如何提高滑动性能?
- 进程(一) 1.1 Android中异步处理大杀器——AsyncTask
- 像写Flutter一样开发Android原生应用
- Android上超级好用的前端调试方法(adb reverse)
- [Android]-图片JNI(C++\Java)高斯模糊的实现与比较
- Android(安卓)入门第十讲02-广播(广播概述,使用方法(系统广播,自定义
- Android(java方法)上实现mp4的分割和拼接 (二)