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();
	}


相关文章
|
2天前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
13 1
|
3天前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
17 1
|
18天前
|
数据处理 开发工具 数据安全/隐私保护
Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路
本文探讨了Android平台上推流模块中添加文字与PNG水印的技术演进。自2015年起,为了满足应急指挥及安防领域的需求,逐步发展出三代水印技术:第一代为静态文字与图像水印;第二代实现了动态更新水印内容的能力,例如实时位置与时间信息;至第三代,则优化了数据传输效率,直接使用Bitmap对象传递水印数据至JNI层,减少了内存拷贝次数。这些迭代不仅提升了用户体验和技术效率,也体现了开发者追求极致与不断创新的精神。
|
22天前
|
自然语言处理 定位技术 API
Android经典实战之如何获取图片的经纬度以及如何根据经纬度获取对应的地点名称
本文介绍如何在Android中从图片提取地理位置信息并转换为地址。首先利用`ExifInterface`获取图片内的经纬度,然后通过`Geocoder`将经纬度转为地址。注意操作需在子线程进行且考虑多语言支持。
83 4
|
1天前
|
存储 监控 数据库
Android经典实战之OkDownload的文件分段下载及合成原理
本文介绍了 OkDownload,一个高效的 Android 下载引擎,支持多线程下载、断点续传等功能。文章详细描述了文件分段下载及合成原理,包括任务创建、断点续传、并行下载等步骤,并展示了如何通过多种机制保证下载的稳定性和完整性。
7 0
|
24天前
|
XML 前端开发 Android开发
Android经典实战之Kotlin中实现圆角图片和圆形图片
本文介绍两种实现圆角图像视图的方法。第一种是通过自定义Kotlin `AppCompatImageView`,重写`onDraw`方法使用`Canvas`和`Path`进行圆角剪裁。第二种利用Android Material库中的`ShapeableImageView`,简单配置即可实现圆角效果。两种方法均易于实现且提供动态调整圆角半径的功能。
15 0
|
3月前
|
Java 开发工具 Android开发
详细解读Android开发DNK开发将.c文件打包成os
详细解读Android开发DNK开发将.c文件打包成os
22 0
|
Android开发
Android 方法数超过64k、编译OOM、编译过慢解决方案。
 目前将项目中的leancloud的即时通讯改为环信的即时通讯。当引入easeui的时候 出现方法数超过上限的问题。  搜索一下问题,解决方法很简单。  这里简单记录一下,顺序记录一下此解决方案导致的另一个问题。
1218 0
|
2天前
|
测试技术 Linux Android开发
探索安卓开发之旅:从初学者到专家
【8月更文挑战第29天】本文是一篇为初学者和有一定经验的开发者准备的安卓开发指南。我们将从基础概念开始,逐步深入到高级主题,如自定义视图、性能优化等。无论你是刚刚入门,还是希望提升自己的技能,这篇文章都将为你提供有价值的信息和建议。让我们一起踏上这段激动人心的旅程吧!
|
1天前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。