本文转载于:http://www.cnblogs.com/tianzhijiexian/p/4254110.htmlBitmap是引起OOM的罪魁祸首之一,当我们从网络上下载图片的时候无法知道网络图片的准确大小,所以为了节约内存,一般会在服务器上缓存一个缩略图,提升下载速度。除此 ...
本文转载于:http://www.cnblogs.com/tianzhijiexian/p/4254110.html
Bitmap是引起OOM的罪魁祸首之一,当我们从网络上下载图片的时候无法知道网络图片的准确大小,所以为了节约内存,一般会在服务器上缓存一个缩略图,提升下载速度。除此之外,我们还可以在本地显示图片前将图片进行压缩,使其完全符合imageview的大小,这样就不会浪费内存了。
一、思路
思路:计算出要显示bitmap的imageview大小,根据imageview的大小压缩bitmap,最终让bitmap和imageview一样大。
二、获得ImageView的宽高
int
android.view.View.getWidth() // 返回:The width of your view, in pixels.
view.post(new Runnable() { @Override public void run() { // TODO 自动生成的方法存根 } })
三、通过BitmapFactory得到压缩后的bitmap
3.1 BitmapFactory.Options
BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。
- SD卡中的图片可以使用decodeFile方法:Bitmap android.graphics.BitmapFactory.decodeFile(String pathName,Options opts)
- 网络上的图片可以使用decodeStream方法:Bitmap android.graphics.BitmapFactory.decodeResource(Resources res, int id, Options opts)
- 资源文件中的图片可以使用decodeResource方法:Bitmapandroid.graphics.BitmapFactory.decodeResource(Resources res, int id, Options opts)
这些方法都会为一个bitmap分配内存,如果你的图片太大就很容易造成bitmap。为此,这里面都可以传入一个BitmapFactory.Options对象,用来进行配置。
BitmapFactory.Options options = new BitmapFactory.Options();
BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(getResources(), R.id.myimage, options);int imageHeight = options.outHeight;int imageWidth = options.outWidth;String imageType = options.outMimeType;
3.2 初步计算压缩比率
BitmapFactory.Options中有个inSampleSize属性,可以理解为压缩比率。设定好压缩比率后,调用上面的decodexxxx()就能得到一个缩略图了。比如inSampleSize=4,载入的缩略图是原图大小的1/4。
为了避免OOM异常,最好在解析每张图片的时候都先检查一下图片的大小,以下几个因素是我们需要考虑的:
预估一下加载整张图片所需占用的内存
为了加载这一张图片你所愿意提供多少内存
用于展示这张图片的控件的实际大小
当前设备的屏幕尺寸和分辨率
比如,你的ImageView只有128*96像素的大小,只是为了显示一张缩略图,这时候把一张1024*768像素的图片完全加载到内存中显然是不值得的。比如我们有一张2048*1536像素的图片,将inSampleSize的值设置为4,就可以把这张图片压缩成512*384像素。原本加载这张图片需要占用13M的内存,压缩后就只需要占用0.75M了(假设图片是ARGB_8888类型,即每个像素点占用4个字节)。
同理,假设原图是1500x700的,我们给缩略图留出的空间是100x100的。那么inSampleSize=min(1500/100, 700/100)=7。我们可以得到的缩略图是原图的1/7。
下面的代码可以用来计算这个inSampleSize的值:
/** * @description 计算图片的压缩比率 * * @param options 参数 * @param reqWidth 目标的宽度 * @param reqHeight 目标的高度 * @return */ private 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 = width / 2; while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
/** * @description 从Resources中加载图片 * * @param res * @param resId * @param reqWidth * @param reqHeight * @return */ public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 设置成了true,不占用内存,只获取bitmap宽高 BitmapFactory.decodeResource(res, resId, options); // 第一次解码,目的是:读取图片长宽 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 调用上面定义的方法计算inSampleSize值 // 使用获取到的inSampleSize值再次解析图片 options.inJustDecodeBounds = false; Bitmap src = BitmapFactory.decodeResource(res, resId, options); // 产生一个稍大的缩略图 return createScaleBitmap(src, reqWidth, reqHeight, options.inSampleSize); // 通过得到的bitmap进一步产生目标大小的缩略图 } /** * @description 从SD卡上加载图片 * * @param pathName * @param reqWidth * @param reqHeight * @return */ public static Bitmap decodeSampledBitmapFromFile(String pathName, int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; Bitmap src = BitmapFactory.decodeFile(pathName, options); return createScaleBitmap(src, reqWidth, reqHeight, options.inSampleSize); }
这两个方法的过程完全一致,产生一个option对象,设置inJustDecodeBounds参数,开始第一次解析bitmap,获得bitmap的宽高数据,然后通过宽高数据计算缩略图的比率。得到缩略图比率后,我们把inJustDecodeBounds设置为false,开始正式解析bitmap,最终得到的是一个可能比想要的缩略图略大的bitmap。最后一部是检查bitmap是否是我们想要的bitmap,如果是就返回,不是的话就开始拉伸,总之最终会返回一个完全符合imageview大小的bitmap,不浪费一点点内存。
完整的工具类代码如下:
public void butonListener(View v) { switch (v.getId()) { case R.id.original_button: loadBitmap(false); // 加载原图 break; case R.id.clip_button: loadBitmap(true); // 加载缩略图 break; } } public void loadBitmap(boolean exactable) { int bmSize = 0; Bitmap bm = null; if (exactable) { // 通过工具类来产生一个符合ImageView的缩略图,因为ImageView的大小是50x50,所以这里得到的缩略图也应该是一样大小的 bm = BitmapUtils.decodeSampledBitmapFromResource(getResources(), R.drawable.saber, iv.getWidth(), iv.getHeight()); } else { // 直接加载原图 bm = BitmapFactory.decodeResource(getResources(), R.drawable.saber); } iv.setImageBitmap(bm); bmSize += bm.getByteCount(); // 得到bitmap的大小 int kb = bmSize / 1024; int mb = kb / 1024; Toast.makeText(this, "bitmap size = " + mb + "MB" + kb + "KB", Toast.LENGTH_LONG).show(); }
根据点击不同的按钮,触发不同的方法,最终把得到的bitmap放入imageview,并且显示当前的bitmap大小。运行后可以发现,经过压缩算法得到的bitmap要小很多,更加节约内存。
结果:原图:1M+;缩略图:156kb。
注意:当你的imageview远远小于bitmap原图大小的时候这种压缩算法十分有效,但是如果的bitmap和imageview大小差不多,你会发现这个算法的作用就不那么明显了,而且不要认为用了压缩就永远不会出现OOM了。
PS:实际使用中我们的bitmap经常是大于imageview的,所以推荐采用此方法。
源码下载:http://download.csdn.net/detail/shark0017/8402227
参考自:
http://www.cnblogs.com/kobe8/p/3877125.html
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
http://stormzhang.com/android/2013/11/20/android-display-bitmaps-efficiently/
原标题:(转)根据ImageView的大小来压缩Bitmap,避免OOM
关键词:ie
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:
admin#shaoqun.com
(#换成@)。