android蛋疼的OOM

  该技术贴仅仅作为一个总结的帖子吧,避免日后自己忘记了,好久没有写博客了,看着自己关注的大牛们依然持之以恒的更新着自己的博客,很是惭愧啊。相信很多人在实际工作中遇到Android某个类别的问题,很自然就会查看目前网上主流的解决方式,并逐一验证是否可行。

      遇到的问题:一个瀑布流展示的客户端,后台传过来的图片都是用户自己上传的照片或者自拍照,图片源是杂七杂八的什么分辨率什么尺寸的图片都有,害苦了前端工程师。

      1.首先参考的是guolinAndroid高效加载大图、多图解决方案,有效避免程序OOM

        文章中说的很明确:BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。

      对应我们的肯定是网络上的图片可以使用decodeStream方法

     2.中间过程省了,因为网上提到的如何避免OOM的文章基本都参考了,写代码时也有意的去避免不规范的书写,比如获得ARGB_4444色彩模式的图片,减少图片占用字节数,节省内存,默认的Config.ARGB8888,及时释放bitmap.recycle();但是还是建议不要盲目调用recycle,防止自己给自己挖坑。在使用一个自己都不知道啥时候就已回收的bitmap时,ImageView.onDraw时报错。

        在第一步确认了自己的方向后,那就开始做吧,众所周知的是既然图片是不规则的大小,那么要先按照自己将要显示的尺寸进行缩放。

        在Android开发中加载sdcard上的大图片到内存时容易导致OOM异常,常见的解决办法是 基于BitmapFactory.Options类提供的方法定义指定的解码方式, 设置inJustDecodeBounds属性为true,避免分配内存,返回一个null的Bitmap对象(包含outWidth,outHeightandoutMimeType),然后读取图片的尺寸和类型。再根据屏幕的高和宽对图片进行缩放,最后将缩放的图片加载到内存,主要代码如下     

	public static Bitmap decodeSampledBitmapFromFd(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);
	}

        对应网络的话将decodeFile换乘decodeStream参数换乘输入流即可,但是最终你拿到的bitmap是null

网上有人解答,其实很简单你第一次获取缩放比时读了一遍流,此时的流已经到了末尾,和读文件一个道理,那个游标到了文章的末尾,再次读时肯定就是空。这时候有网上的解决方案说用这个:

private byte[] readStream(InputStream inStream) throws Exception {
			byte[] buffer = new byte[1024];
			int len = -1;
			ByteArrayOutputStream outStream = new ByteArrayOutputStream();
			while ((len = inStream.read(buffer)) != -1) {
				outStream.write(buffer, 0, len);
			}
			byte[] data = outStream.toByteArray();
			outStream.close();
			inStream.close();
			return data;
		}
	 private Bitmap decodeBitmapFromBytes(byte[] ib, int reqWidth) {
		 // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
		 final BitmapFactory.Options options = new BitmapFactory.Options();
		 options.inJustDecodeBounds = true;
		 BitmapFactory.decodeByteArray(ib, 0, ib.length, options);
		 // 2.计算原图宽高比例
		 int srcWidth = options.outWidth;
		 int srcHeight = options.outHeight;
		 float h_divide_w = ((float) srcHeight) / srcWidth;
		 int reqHeight = (int) (reqWidth * h_divide_w);
		 // 调用上面定义的方法计算inSampleSize值
		 options.inSampleSize = calculateInSampleSize(options, reqWidth,
		 reqHeight);
		 MyDebug.printI(MiMooConstants.APP_TAG, "压缩比:->> "
		 + options.inSampleSize);
		// // 使用获取到的inSampleSize值再次解析图片
		 options.inJustDecodeBounds = false;
		 Bitmap bm = BitmapFactory
		 .decodeByteArray(ib, 0, ib.length, options);
			MyDebug.printI(MiMooConstants.APP_TAG,
					"图片的大小:->> " + bm.getByteCount() / (1024 * 1024) + " M");
			return bm;
		}

    他的思路是先把流读到字节数组中,当再次读取时我的字节数组肯定还是在的,这样就规避了上面游标到了末尾的问题。这个解决方案在实际运行中并不好还经常报OOM,原因是ByteArrayOutputStream该类是将流读到内存中,会占用内存空间如果你的图片过大,用该类是不合适的,直接一次性读到内存中,不说你也明白OOM是不可避免的。

   针对这个问题参考了:android客户端加载网络大图片如何避免内存溢出 - 劲奇

   作者描述的现象和我遇到的基本一致,按照作者的思路基本解决了我的问题,后面还需要再观察。

   推荐大家尝试...

    如何避免OOM这个问题其实很复杂,也很简单,主流的方式就那么多,越是图片多的APP对你的编码要求越高,谨慎谨慎再谨慎吧。缓存,释放,缩放,bitmap的谨慎使用,如何更好的管理和释放native内存等等

      2016-05-05 刚看到了之前写的这篇文章,这种方案不建议大家使用,网络处理图片这样请求网络两次如果是在瀑布流中处理图片,效果估计会疯掉的。现在处理图片基本都用七牛了,用七牛服务器处理图片还是很猛的,也省的你自己在那里做这些本不该客户端开发关心的。七牛的api函数还是得好好研究下。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值