Android: 缩放图片文件引起的OOM异常

本文介绍了如何通过设置缩放选项和读取缩放后的图片数据来避免内存溢出(OOM)问题,以实现高效图片缩放与存储。以MMS应用为例,详细阐述了其在处理多媒体附件时对图片进行缩放的方法,包括获取原始图片尺寸、设置缩放选项、读取缩放图片数据并保存为文件等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传输文件,或者设置头像,我们一般都会检查原始图片的大小,作缩放处理。

常用的Java版缩放图片代码:

  1. public Bitmap getZoomImage(Bitmap src, int desW, int desH) 
  2.     Bitmap desImg = null
  3.     int srcW = src.getWidth(); // 原始图像宽 
  4.     int srcH = src.getHeight(); // 原始图像高 
  5.     int[] srcBuf = new int[srcW * srcH]; // 原始图片像素信息缓存 
  6.      
  7.     src.getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH); 
  8.      
  9.     // 计算插值表 
  10.     int[] tabY = new int[desH]; 
  11.     int[] tabX = new int[desW]; 
  12.      
  13.     int sb = 0
  14.     int db = 0
  15.     int tems = 0
  16.     int temd = 0
  17.     int distance = srcH > desH ? srcH : desH; 
  18.     for (int i = 0; i <= distance; i++) 
  19.     {/* 垂直方向 */ 
  20.         tabY[db] = sb; 
  21.         tems += srcH; 
  22.         temd += desH; 
  23.         if (tems > distance) 
  24.         { 
  25.             tems -= distance; 
  26.             sb++; 
  27.         } 
  28.         if (temd > distance) 
  29.         { 
  30.             temd -= distance; 
  31.             db++; 
  32.         } 
  33.     } 
  34.      
  35.     sb = 0
  36.     db = 0
  37.     tems = 0
  38.     temd = 0
  39.     distance = srcW > desW ? srcW : desW; 
  40.      
  41.     for (int i = 0; i <= distance; i++) 
  42.     {/* 水平方向 */ 
  43.         tabX[db] = (short) sb; 
  44.         tems += srcW; 
  45.         temd += desW; 
  46.         if (tems > distance) 
  47.         { 
  48.             tems -= distance; 
  49.             sb++; 
  50.         } 
  51.         if (temd > distance) 
  52.         { 
  53.             temd -= distance; 
  54.             db++; 
  55.         } 
  56.     } 
  57.      
  58.     // 生成放大缩小后图形像素 
  59.     int[] desBuf = new int[desW * desH]; 
  60.     int dx = 0
  61.     int dy = 0
  62.     int sy = 0
  63.     int oldy = -1
  64.      
  65.     for (int i = 0; i < desH; i++) 
  66.     { 
  67.         if (oldy == tabY[i]) 
  68.         { 
  69.             System.arraycopy(desBuf, dy - desW, desBuf, dy, desW); 
  70.         } 
  71.         else 
  72.         { 
  73.             dx = 0
  74.             for (int j = 0; j < desW; j++) 
  75.             { 
  76.                 desBuf[dy + dx] = srcBuf[sy + tabX[j]]; 
  77.                 dx++; 
  78.             } 
  79.             sy += (tabY[i] - oldy) * srcW; 
  80.         } 
  81.         oldy = tabY[i]; 
  82.         dy += desW; 
  83.     } 
  84.     // 生成图片 
  85.     desImg = Bitmap.createBitmap(desBuf, desW, desH, Bitmap.Config.ARGB_8888); 
  86.      
  87.     return desImg; 
 

常用的Android版缩放图片代码:

  1. ContentResolver cr = this.getContentResolver(); 
  2. try 
  3.     InputStream in = cr.openInputStream(uri); 
  4.     Bitmap bitmap = BitmapFactory.decodeStream(in); 
  5.     try 
  6.     { 
  7.         in.close(); 
  8.     } 
  9.     catch (IOException e) 
  10.     { 
  11.         e.printStackTrace(); 
  12.     } 
  13.     if(null  == bitmap) 
  14.     { 
  15.         Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000); 
  16.     } 
  17.     //原始图片的尺寸 
  18.     int bmpWidth  = bitmap.getWidth(); 
  19.     int bmpHeight = bitmap.getHeight(); 
  20.      
  21.     //缩放图片的尺寸 
  22.     float scaleWidth  = (float) 40 / bmpWidth; 
  23.     float scaleHeight = (float) 40 / bmpHeight; 
  24.     Matrix matrix = new Matrix(); 
  25.     matrix.postScale(scaleWidth, scaleHeight); 
  26.      
  27.     //产生缩放后的Bitmap对象 
  28.     Bitmap resizeBitmap = Bitmap.createBitmap( 
  29.         bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false); 
  30.     bitmap.recycle(); 
  31.     //Bitmap to byte[] 
  32.     byte[] photoData = Bitmap2Bytes(resizeBitmap); 
  33.      
  34.     //save file 
  35.     String fileName = "/sdcard/test.jpg"
  36.     FileUtil.writeToFile(fileName, photoData); 
  37.      
  38.     //save photo check sum to db 
  39.     DataCenter.GetInstance().ModifyIMMUser(); 
  40.     //refresh ImageView 
  41. catch (FileNotFoundException exp) 
  42.     exp.printStackTrace(); 
 

如果图片非常大,在执行BitmapFactory.decodeStream的时候就会抛出OOM异常。

我们来看看系统应用MMS是如何处理的,SMS添加了多媒体附件后就作MMS处理了,当附加文件原图超过300K,也会做个缩放处理,具体参考:com.android.mms.ui/.UriImage:

  1. package com.android.mms.ui; 
  2. public class UriImage 
  3.     private int mWidth; 
  4.     private int mHeight; 
  5.     ... ... 
  6.     // 
  7.     private void decodeBoundsInfo() 
  8.     { 
  9.         InputStream input = null
  10.         try 
  11.         { 
  12.             input = mContext.getContentResolver().openInputStream(mUri); 
  13.             BitmapFactory.Options opt = new BitmapFactory.Options(); 
  14.             opt.inJustDecodeBounds = true;//只描边,不读取数据 
  15.             BitmapFactory.decodeStream(input, null, opt); 
  16.             mWidth = opt.outWidth; 
  17.             mHeight = opt.outHeight; 
  18.         } 
  19.         catch (FileNotFoundException e) 
  20.         { 
  21.             // Ignore 
  22.             Log.e(TAG, "IOException caught while opening stream", e); 
  23.         } 
  24.         finally 
  25.         { 
  26.             if (null != input) { 
  27.                 try
  28.                     input.close(); 
  29.                 } catch (IOException e) { 
  30.                     // Ignore 
  31.                     Log.e(TAG, "IOException caught while closing stream", e); 
  32.                 } 
  33.             } 
  34.         } 
  35.     } 
  36.     private byte[] getResizedImageData(int widthLimit, int heightLimit) 
  37.     { 
  38.         int outWidth = mWidth; 
  39.         int outHeight = mHeight; 
  40.         int s = 1
  41.         while ((outWidth / s > widthLimit) || (outHeight / s > heightLimit)) 
  42.         { 
  43.             s *= 2
  44.         } 
  45.         //先设置选项 
  46.         BitmapFactory.Options options = new BitmapFactory.Options(); 
  47.         //returning a smaller image to save memory. 
  48.         options.inSampleSize = s; 
  49.         InputStream input = null
  50.         try 
  51.         { 
  52.             input = mContext.getContentResolver().openInputStream(mUri); 
  53.             Bitmap b = BitmapFactory.decodeStream(input, null, options);//注意看options的用法 
  54.             if (b == null) { 
  55.                 return null
  56.             } 
  57.             ByteArrayOutputStream os = new ByteArrayOutputStream(); 
  58.             b.compress(CompressFormat.JPEG, MessageUtils.IMAGE_COMPRESSION_QUALITY, os); 
  59.             return os.toByteArray(); 
  60.         } catch (FileNotFoundException e) { 
  61.             Log.e(TAG, e.getMessage(), e); 
  62.             return null
  63.         } finally
  64.             if (input != null) { 
  65.                 try
  66.                     input.close(); 
  67.                 } catch (IOException e) { 
  68.                     Log.e(TAG, e.getMessage(), e); 
  69.                 } 
  70.             } 
  71.         } 
  72.     } 
  73.     ... ... 
 

可以看出,MMS应用的方法是:先设置缩放选项,再读取缩放的图片数据到内存,规避了内存引起的OOM。

修改后的代码:

  1.     ContentResolver cr = this.getContentResolver(); 
  2.     try 
  3.     { 
  4.         InputStream in = cr.openInputStream(uri); 
  5.            BitmapFactory.Options options = new BitmapFactory.Options(); 
  6.            options.inJustDecodeBounds = true
  7.            BitmapFactory.decodeStream(in, null, options); 
  8.         try 
  9.         { 
  10.     in.close(); 
  11.         catch (IOException e) 
  12.         { 
  13.     e.printStackTrace(); 
  14.            int mWidth = options.outWidth; 
  15.            int mHeight = options.outHeight; 
  16.             
  17.            int sWidth  = 40
  18.            int sHeight = 40
  19.             
  20.         int s = 1
  21.         while ((mWidth / s > sWidth * 2) || (mHeight / s > sHeight * 2)) 
  22.         { 
  23.             s *= 2
  24.         } 
  25.            options = new BitmapFactory.Options(); 
  26.         options.inSampleSize = s; 
  27.         in = cr.openInputStream(uri); 
  28.         Bitmap bitmap = BitmapFactory.decodeStream(in, null, options); 
  29.         try 
  30.         { 
  31.     in.close(); 
  32.         catch (IOException e) 
  33.         { 
  34.     e.printStackTrace(); 
  35.         if(null  == bitmap) 
  36.         { 
  37.             Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000); 
  38.             return
  39.         } 
  40.         //原始图片的尺寸 
  41.         int bmpWidth  = bitmap.getWidth(); 
  42.         int bmpHeight = bitmap.getHeight(); 
  43.          
  44.         //缩放图片的尺寸 
  45.         float scaleWidth  = (float) sWidth / bmpWidth; 
  46.         float scaleHeight = (float) sHeight / bmpHeight; 
  47.         Matrix matrix = new Matrix(); 
  48.         matrix.postScale(scaleWidth, scaleHeight); 
  49.          
  50.         //产生缩放后的Bitmap对象 
  51.         Bitmap resizeBitmap = Bitmap.createBitmap( 
  52.             bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false); 
  53.         bitmap.recycle(); 
  54.                 Bitmap resizeBitmap = bitmap; 
  55.         //Bitmap to byte[] 
  56.         byte[] photoData = bitmap2Bytes(resizeBitmap); 
  57.          
  58.         //save file 
  59.         String fileName = "/sdcard/test.jpg"
  60.         FileUtil.writeToFile(fileName, photoData); 
 

  1. private byte[] bitmap2Bytes(Bitmap bm) 
  2.     ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
  3.     bm.compress(Bitmap.CompressFormat.JPEG, 100, baos); 
  4.     return baos.toByteArray(); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值