转载请标明:http://blog.csdn.net/lsyz0021/article/details/51295402

我们项目中经常会加载图片,有时候如果加载图片过多的话,小则导致程序很卡,重则OOM导致App挂了,今天翻译https://developer.Android.com/training/displaying-bitmaps/index.html,学习Google高效加载大图片的方法。

图片有各种形状和大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小。比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨率高得多。大家应该知道,我们编写的应用程序都是有一定内存限制的,程序占用了过高的内存就容易出现OOM(OutOfMemory)异常。我们可以通过下面的代码看出每个应用程序最高可用内存是多少。

[java] view plaincopy
  1. <span style="font-size:18px;">int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024/1024);</span>

现在大部分手机都是32M,既然知道了每个app分配的内存,所以就要计算好加载多少图片而不导致出现OOM,所以要计算每张图片所占用的内存是多少。

Android中计算一张图片所占内存大小方法:图片长*宽*所占像素字节数,而像素字节数Android中也就四种,

1:ALPHA_8 占1个字节

2:ARGB_4444 占2个字节

3:ARGB_8888 占4个字节

4:RGB_565  占2个字节

:ARGB指的是一种色彩模式,里面A代表Alpha,R表示red,G表示green,B表示blue,其实所有的可见色都是红绿蓝组成的,所以红绿蓝又称为三原色。
A  R  G  B
透明度 红色 绿色 蓝色

而这些字节数是可以通过bitmap对象去设置的,bitmap.setConfig(Bitmap.Config.ARGB_4444);如果这是个定值,那么要改变一个张图片的大小,就只能改宽或者高了,如果只改高,宽不变的话,就会造成图片变形,因此一般都是一起改动,所以图片要缩放,而缩放时根据屏幕的宽和高来缩放的,因为Android设备很多,每个屏幕的宽和高也不一样,这样就能达到加载大图片避免OOM。

[java] view plaincopy
  1. BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。比如SD卡中的图片可以使用decodeFile方法,网络上的图片可以使用decodeStream方法,资源文件中的图片可以使用decodeResource方法。这些方法会尝试为已经构建的bitmap分配内存,这时就会很容易导致OOM出现。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。如下代码所示:
[java] view plaincopy
  1. BitmapFactory.Options options = new BitmapFactory.Options();
  2. options.inJustDecodeBounds = true;
  3. BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
  4. int imageHeight = options.outHeight;
  5. int imageWidth = options.outWidth;
  6. String imageType = options.outMimeType;

比如一张480*800的图片直接加载到内存中,如果图片很多那就很容易造成OOM,那就必须压缩,比如按1/8进行压缩,压缩后得到的60*100,如果从服务器获取的图片规格不一样,那压缩后现在在ImageView上肯定不一样,因为ImageView宽和高肯定是设置成wrap_content。

Android系统的手机在系统底层指定了堆内存的上限值,大部分手机的缺省值是16MB,不过也有些高配置的机型是24MB的,所以我们的程序在申请内存空间时,为了确保能够成功申请到内存空间,应该保证当前已分配的内存加上当前需要分配的内存值的总大小不能超过当前堆的最大内存值。由于内存管理上将外部内存完全当成了当前堆的一部分,也就是说Bitmap对象通过栈上的引用来指向堆上的Bitmap对象,而堆上的Bitmap对象又对应了一个使用了外部存储的native图像,也就是实际上使用的字节数组byte[]来存储的位图信息,因此解码之后的Bitmap的总大小就不能超过8M了。

解决这类问题的最根本的,最有效的办法就是,使用完bitmap之后,调用bitmap对象的recycle()方法释放所占用的内存,以便于下一次使用。

下面是网上找到的一些常用的优化办法,但是基本上都不能从本质上解决问题。

1.设置系统的最小堆大小:

[java] view plaincopy
  1. int newSize = 4 * 1024 * 1024 ; //设置最小堆内存大小为4MB
  2. VMRuntime.getRuntime().setMinimumHeapSize(newSize);
  3. VMRuntime.getRuntime().setTargetHeapUtilization(0.75); // 设置堆内存的利用率为75%

补充说明:堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,当堆内存实际的利用率偏离设定的值的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。比如初始的HEAP是4M大小,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。

2.对图片的大小进行压缩控制

[java] view plaincopy
  1. BitmapFactory.Options options = new BitmapFactory.Options();
  2. options.inSampleSize = 2; //图片宽高都为原来的二分之一,即图片为原来的四分之一
  3. Bitmap bitmap = BitmapFactory.decodeFile("/mnt/sdcard/a.jpg",options);

补充说明:这种方法只是对图片做了一个缩放处理,降低了图片的分辨率,在需要保证图片质量的应用中不可取。

3.设置临时存储空间

[java] view plaincopy
  1. BitmapFactory.Options options = new BitmapFactory.Options();
  2. options.inTempStorage = new byte[1024*1024*5]; //5MB的临时存储空间
  3. Bitmap bm = BitmapFactory.decodeFile("/mnt/sdcard/a.jpg",options);

补充说明:从创建Bitmap的C++底层代码BitmapFactory.cpp中的处理逻辑来看,如果option不为null的话,那么会优先处理option中设置的各个参数,假设当前你设置option的inTempStorage为1024*1024*4(4M)大小的话,而且每次解码图像时均使用该option对象作为参数,那么你的程序极有可能会提前失败,经过测试,如果使用一张大小为1.03M的图片来进行解码,如果不使用option参数来解码,可以正常解码四次,也就是分配了四次内存,而如果使用option的话,就会出现内存溢出错误,只能正常解码两次。Options类有一个预处理参数,当你传入options时,并且指定临时使用内存大小的话,Android将默认先申请你所指定的内存大小,如果申请失败,就会先抛出内存溢出错误。而如果不指定内存大小,系统将会自动计算,如果当前还剩3M空间大小,而解码只需要2M大小,那么在缺省情况下将能解码成功,而在设置inTempStorage大小为4M的情况下就将出现内存溢出错误。所以,通过设置Options的inTempStorage大小也不能从根本上解决大图像解码的内存溢出问题。

总之再做android开发时,出现内存溢出是属于系统底层限制,只要解码需要的内存超过系统可分配的最大内存值,那么内存溢出错误必然会出现。

对于Android4.0以下的手机我们还要这么处理:

Android平台在图片处理方面经常会出现OOM的问题,一直被这个问题所困扰,在这方面也搜集了许多的资料,今天仅仅针对Android平台的Bitmap说事儿,今后再对内存的问题做详细的探讨,android平台对图片解码这块确实设置的有内存上限,在解码Bitmap的时候android平台会对其需要占用的内存进行Check,一旦需要的内存超越上限,则直接报错,下面援引邓凡平老师的解释:

createBitmap好像有一个参数,可以绕过虚拟机的堆栈检查。内存报错其实是先检查是否超过限制,比如最大16M,你要分配32M,检查的时候超标,则会报错。除此之外,没有别的办法可以解决该问题。我们当时测试了30M的图片分配,如果不加该参数,则必然报错。加了就没事了。该参数是hidden的。你必须用源码编译才可以。而只有那个函数可以用。其余都用不了。该参数是BitmapFactory.options类的public boolean inNativeAlloc。不过4.0已经去掉该参数了。所以这个办法可能也不行。

你最好分析下你的图片尺寸大小,实在不行的话,把buffer传递给native层,然后在native层做修改。记住我刚才说的,内存检查是Bitmap自己去check的。native层其实malloc/new多大内存都无所谓,只要你不去check。

这个参数就是:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inNativeAlloc = true;
接下来我查证了SDK的文档,其中BitmapFactory.Options中并没有inNativeAlloc这个参数,为了查证这个参数,我继续查看了Android系统源代码的BitmapFactory部分,在其中找到了该参数,以下是对该参数的描述:

/*** 
        * Normally bitmap allocations count against the dalvik heap, which 
     * means they help trigger GCs when a lot have been allocated. However, 
     * in rare cases, the caller may want to allocate the bitmap outside of 
        * that heap. To request that, set inNativeAlloc to true. In these 
        * rare instances, it is solely up to the caller to ensure that OOM is 
        * managed explicitly by calling bitmap.recycle() as soon as such a 
        * bitmap is no longer needed. 
     * 
     * @hide pending API council approval 
     */

我在Android Developers 论坛上找到了一段话:
> On Wed, Jun 8, 2011 at 10:17 AM, Erik R <ejwrobert...@gmail.com> wrote:
> > I'm working on a simple image manipulation app that requires opening
> > bitmaps at full resolution, which of course results in OutOfMemory
> > issues. I know that the short answer is to simply use less memory via
> > BitmapFactory's inSampleSize Option to downsample the bitmap, but for
> > this app I really would like to retain the full resolution for
> > editing. One solution I have been investigating is loading the bitmap
> > entirely on the native heap, after learning that the memory limitation
> > is only imposed within the Dalvik VM heap. A look into the Android
> > source code revealed that this is already the case... BitmapFactory
> > uses native methods to load a bitmap on the native heap, while
> > maintaining a reference to it on the VM heap. The issue then is that
> > it appears the native memory used by the bitmap is actually counted
> > against the available VM memory, when it really isn't there. A look
> > into the stock Camera and Gallery apps' source revealed that they get
> > around this by using an additional BitmapFactory Option,
> > inNativeAlloc. I was able to find this field in the Java code for
> > BitmapFactory:

但是这个参数是hidden的,在使用中需要带源码编译才可以。

总之,使用这个参数确实能避开内存检查,并且系统自带的图片浏览器中也使用了这个参数,但是注意,这个参数仅仅在4.0以下平台适用。
解决方式:
参考:http://blog.csdn.net/zhiying201039/article/details/8581982
参考:http://blog.csdn.net/wxg630815/article/details/7434833

参考:http://blog.csdn.net/coderinchina/article/details/40964205

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

Android开发解决加载图片OOM问题(非常全面 兼顾4 0以下系统)(by 星空武哥)相关推荐

  1. [转]Android有效解决加载大图片时内存溢出的问题

    http://hi.baidu.com/%D6%C7%B4%EF%B8%DF%D4%B6lee/blog/item/7bd659af3f40dc1d4b36d68d.html 尽量不要使用setIma ...

  2. 又优化了一下 Android ListView 异步加载图片

    写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好: http://www.iteye.com/topic/685986 我也是因为看 ...

  3. Android加载图片OOM错误解决方式

    前几天做项目的时候,甲方要求是PAD (SAMSUNG P600 10.1寸 2560*1600)的PAD上显示高分辨率的大图片. SQLITE採用BOLD方式存储图片,这个存取过程就不说了哈,网上一 ...

  4. android 加载网络bitmap图片 oom 简书_Android常见问题--ImageView加载图片OOM

    开发中给ImageView加载一个高质量图片时,APP抛出了"Canvas: trying to draw too large(840253440bytes) bitmap."的异 ...

  5. Android开发 - ImageView加载Base64编码的图片

    在我们开发应用的过程中,并不是所有情况下都请求图片的URL或者加载本地图片,有时我们需要加载Base64编码的图片.这种情况出现在服务端需要动态生成的图片,比如: 二维码 图形验证码 - 这些应用场景 ...

  6. android 动画 图片 内存溢出,Android有效解决加载大图片时内存溢出的问题

    尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过ja ...

  7. Android ListView异步加载图片乱序问题,原因分析及解决方案

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/45586553 在Android所有系统自带的控件当中,ListView这个控件算是 ...

  8. Android之glide加载图片圆角效果

    1 问题 Android加载图片需要圆角化,有什么简单粗暴的方法吗?当然有,用我们的神器glide 2 解决办法 1)简单办法 ImageView imageView = (ImageView)hel ...

  9. android listview 异步加载图片并防止错位

    网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertVie ...

最新文章

  1. Bit-Scalable Deep Hashing with Regularized Similarity Learning for Image Retrieval and Person Re-ide
  2. 超图预览osgb格式倾斜摄影文件
  3. 机器学习笔记: Upsampling, U-Net, Pyramid Scene Parsing Net
  4. 一个javascript框架应有的功能
  5. 误码率越高越好还是越低越好_夜间护理步骤越多越好还是越少越好?NFF
  6. python集合用法_Python 集合(Set)
  7. linux 网络相关,Linux系统管理员必备的21个网络相关监控
  8. ln: failed to create symbolic link ‘/usr/bin/mysql’: File exists
  9. android资源收藏贴[持续更新]
  10. 用计算机乘九位数怎么用,用计算器计算
  11. linux查看所有目录
  12. 阿里云(企业云解析DNS)让你的博客飞起来
  13. 评分卡Bad rate单调性问题
  14. 大恒相机标定with MATLAB
  15. kubernetes集群搭建Zabbix监控平台
  16. Android JNI开发入门与实战
  17. Android 10.0 移动网络默认为4G
  18. 【干货】-- 带你抓取并分析知乎高评分电影
  19. 看到“java单例模式”脑壳疼,学会这几招分分钟搞定
  20. 南昌不翻车 Codeforces Round #571 (Div. 2) C,D

热门文章

  1. 论坛无法上传文件,就把它变成文本文件再发帖留言
  2. poweroff 和 单用户模式
  3. 同名文件替换怎么恢复,恢复同名文件
  4. 服务器文件怎么替换,怎么替换服务器数据库文件
  5. 如何打造元宇宙中的数据银行DataRights
  6. 【Android Studio】修改AVD文件位置,释放C盘空间
  7. 时隔4年,这个Windows必备神器升级了!
  8. 揭秘快速提升alexa排名的18种有效方法-#来秀美#
  9. 五年Java面试常问
  10. rbw数字信号处理_【有奖】示波器,实时频谱仪?MXR让您鱼与熊掌兼得!