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

http://dev.10086.cn/cmdn/wiki/index.php?doc-view-5037.html


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

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

	public Bitmap getZoomImage(Bitmap src, int desW, int desH)
	{
		Bitmap desImg = null;
		int srcW = src.getWidth(); // 原始图像宽
		int srcH = src.getHeight(); // 原始图像高
		int[] srcBuf = new int[srcW * srcH]; // 原始图片像素信息缓存
		
		src.getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH);
		
		// 计算插值表
		int[] tabY = new int[desH];
		int[] tabX = new int[desW];
		
		int sb = 0;
		int db = 0;
		int tems = 0;
		int temd = 0;
		int distance = srcH > desH ? srcH : desH;
		for (int i = 0; i <= distance; i++)
		{/* 垂直方向 */
			tabY[db] = sb;
			tems += srcH;
			temd += desH;
			if (tems > distance)
			{
				tems -= distance;
				sb++;
			}
			if (temd > distance)
			{
				temd -= distance;
				db++;
			}
		}
		
		sb = 0;
		db = 0;
		tems = 0;
		temd = 0;
		distance = srcW > desW ? srcW : desW;
		
		for (int i = 0; i <= distance; i++)
		{/* 水平方向 */
			tabX[db] = (short) sb;
			tems += srcW;
			temd += desW;
			if (tems > distance)
			{
				tems -= distance;
				sb++;
			}
			if (temd > distance)
			{
				temd -= distance;
				db++;
			}
		}
		
		// 生成放大缩小后图形像素
		int[] desBuf = new int[desW * desH];
		int dx = 0;
		int dy = 0;
		int sy = 0;
		int oldy = -1;
		
		for (int i = 0; i < desH; i++)
		{
			if (oldy == tabY[i])
			{
				System.arraycopy(desBuf, dy - desW, desBuf, dy, desW);
			}
			else
			{
				dx = 0;
				for (int j = 0; j < desW; j++)
				{
					desBuf[dy + dx] = srcBuf[sy + tabX[j]];
					dx++;
				}
				sy += (tabY[i] - oldy) * srcW;
			}
			oldy = tabY[i];
			dy += desW;
		}
		// 生成图片
		desImg = Bitmap.createBitmap(desBuf, desW, desH, Bitmap.Config.ARGB_8888);
		
		return desImg;
	}

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

ContentResolver cr = this.getContentResolver();
try
{
	InputStream in = cr.openInputStream(uri);
	Bitmap bitmap = BitmapFactory.decodeStream(in);
	try
	{
		in.close();
	}
	catch (IOException e)
	{
		e.printStackTrace();
	}
	if(null  == bitmap)
	{
		Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000);
	}
	//原始图片的尺寸
	int bmpWidth  = bitmap.getWidth();
	int bmpHeight = bitmap.getHeight();
	
	//缩放图片的尺寸
	float scaleWidth  = (float) 40 / bmpWidth;
	float scaleHeight = (float) 40 / bmpHeight;
	Matrix matrix = new Matrix();
	matrix.postScale(scaleWidth, scaleHeight);
	
	//产生缩放后的Bitmap对象
	Bitmap resizeBitmap = Bitmap.createBitmap(
		bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false);
	bitmap.recycle();
	//Bitmap to byte[]
	byte[] photoData = Bitmap2Bytes(resizeBitmap);
	
	//save file
	String fileName = "/sdcard/test.jpg";
	FileUtil.writeToFile(fileName, photoData);
	
	//save photo check sum to db
	DataCenter.GetInstance().ModifyIMMUser();
	//refresh ImageView
}
catch (FileNotFoundException exp)
{
	exp.printStackTrace();
}

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

 

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

package com.android.mms.ui;
public class UriImage
{
    private int mWidth;
    private int mHeight;
    ... ...
    //
    private void decodeBoundsInfo()
    {
        InputStream input = null;
        try
        {
            input = mContext.getContentResolver().openInputStream(mUri);
            BitmapFactory.Options opt = new BitmapFactory.Options();
            opt.inJustDecodeBounds = true;//只描边,不读取数据
            BitmapFactory.decodeStream(input, null, opt);
            mWidth = opt.outWidth;
            mHeight = opt.outHeight;
        }
        catch (FileNotFoundException e)
        {
            // Ignore
            Log.e(TAG, "IOException caught while opening stream", e);
        }
        finally
        {
            if (null != input) {
                try {
                    input.close();
                } catch (IOException e) {
                    // Ignore
                    Log.e(TAG, "IOException caught while closing stream", e);
                }
            }
        }
    }
    private byte[] getResizedImageData(int widthLimit, int heightLimit)
    {
        int outWidth = mWidth;
        int outHeight = mHeight;
        int s = 1;
        while ((outWidth / s > widthLimit) || (outHeight / s > heightLimit))
        {
            s *= 2;
        }
        //先设置选项
        BitmapFactory.Options options = new BitmapFactory.Options();
        //returning a smaller image to save memory.
        options.inSampleSize = s;
        InputStream input = null;
        try
        {
            input = mContext.getContentResolver().openInputStream(mUri);
            Bitmap b = BitmapFactory.decodeStream(input, null, options);//注意看options的用法
            if (b == null) {
                return null;
            }
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            b.compress(CompressFormat.JPEG, MessageUtils.IMAGE_COMPRESSION_QUALITY, os);
            return os.toByteArray();
        } catch (FileNotFoundException e) {
            Log.e(TAG, e.getMessage(), e);
            return null;
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    Log.e(TAG, e.getMessage(), e);
                }
            }
        }
    }
    ... ...
}

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

 

修改后的代码:

 	 				ContentResolver cr = this.getContentResolver();
 	 				try
 	 				{
 	 					InputStream in = cr.openInputStream(uri);
 	 		            BitmapFactory.Options options = new BitmapFactory.Options();
 	 		            options.inJustDecodeBounds = true;
 	 		            BitmapFactory.decodeStream(in, null, options);
 	 					try
 	 					{
							in.close();
						}
 	 					catch (IOException e)
 	 					{
							e.printStackTrace();
						}
 	 		            int mWidth = options.outWidth;
 	 		            int mHeight = options.outHeight;
 	 		            
 	 		            int sWidth  = 40;
 	 		            int sHeight = 40;
 	 		            
 	 			        int s = 1;
 	 			        while ((mWidth / s > sWidth * 2) || (mHeight / s > sHeight * 2))
 	 			        {
 	 			            s *= 2;
 	 			        }
 	 		            options = new BitmapFactory.Options();
 	 			        options.inSampleSize = s;
 	 					in = cr.openInputStream(uri);
 	 					Bitmap bitmap = BitmapFactory.decodeStream(in, null, options);
 	 					try
 	 					{
							in.close();
						}
 	 					catch (IOException e)
 	 					{
							e.printStackTrace();
						}
 	 					if(null  == bitmap)
 	 					{
 	 						Toast.makeText(this, "Head is not set successful,Decode bitmap failure", 2000);
 	 						return ;
 	 					}
 	 					//原始图片的尺寸
 	 					int bmpWidth  = bitmap.getWidth();
 	 					int bmpHeight = bitmap.getHeight();
 	 					
 	 					//缩放图片的尺寸
 	 					float scaleWidth  = (float) sWidth / bmpWidth;
 	 					float scaleHeight = (float) sHeight / bmpHeight;
 	 					Matrix matrix = new Matrix();
 	 					matrix.postScale(scaleWidth, scaleHeight);
 	 					
 	 					//产生缩放后的Bitmap对象
 	 					Bitmap resizeBitmap = Bitmap.createBitmap(
 	 						bitmap, 0, 0, bmpWidth, bmpHeight, matrix, false);
 	 					bitmap.recycle();
// 	 					Bitmap resizeBitmap = bitmap;
 	 					//Bitmap to byte[]
 	 					byte[] photoData = bitmap2Bytes(resizeBitmap);
 	 					
 	 					//save file
 	 					String fileName = "/sdcard/test.jpg";
 	 					FileUtil.writeToFile(fileName, photoData);

	private byte[] bitmap2Bytes(Bitmap bm)
	{
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
		return baos.toByteArray();
	}


上一篇:领课在线教育系统架构升级看看更新了哪些内容


下一篇:FTP/SFTP 文件传输协议