首页 > 其他分享 >Android处理图片OOM的若干方法小结

Android处理图片OOM的若干方法小结

时间:2023-07-28 17:34:35浏览次数:44  
标签:OOM 加载 回收 内存 Android 图片 小结 引用


众所周知,每个Android应用程序在运行时都有一定的内存限制,限制大小一般为16MB或24MB(视平台而定)。因此在开发应用时需要特别关注自身的内存使用量,而一般最耗内存量的资源,一般是图片、音频文件、视频文件等多媒体资源;由于Android系统对音频、视频等资源做了边解析便播放的处理,使用时并不会把整个文件加载到内存中,一般不会出现内存溢出(以下简称OOM)的错误,因此它们的内存消耗问题暂不在本文的讨论范围。本文重点讨论的是图片的内存消耗问题,如果你要开发的是一款图片浏览器应用,例如像Android系统自带的Gallery那样的应用,这个问题将变得尤为突出;如果你开发的是目前的购物客户端,有时候处理不当也会碰到这种问题。

目前碰到的OOM场景,无外乎以下几种情形,不过无论是哪种情形,解决问题的思路都是一致的。

(1)显示单张图片,图片文件体积达到3000*4000级别的时候;

(2)在ListView或Gallery等控件中一次性加载大量图片时;

相关知识介绍

1.颜色模型

常见的颜色模型有RGB、YUV、CMYK等,在大多数图像API中采用的都是RGB模型,Android也是如此;另外,在Android中还有包含透明度Alpha的颜色模型,即ARGB。关于颜色模型更加详细的信息暂不在本文的讨论范围之内。

Android处理图片OOM的若干方法小结_软引用

2.计算机中颜色值的数字化编码

在不考虑透明度的情况下,一个像素点的颜色值在计算机中的表示方法有以下3种:

(1)浮点数编码:比如float: (1.0, 0.5, 0.75),每个颜色分量各占1个float字段,其中1.0表示该分量的值为全红或全绿或全蓝;

(2)24位的整数编码:比如24-bit:(255, 128, 196),每个颜色分量各占8位,取值范围0-255,其中255表示该分量的值为全红或全绿或全蓝;

(3)16位的整数编码:比如16-bit:(31, 45, 31),第1和第3个颜色分量各占5位,取值范围0-31,第2个颜色分量占6位,取值范围0-63;

在Java中,float类型的变量占32位,int类型的变量占32位,short和char类型的变量都在16位,因此可以看出,用浮点数表示法编码一个像素的颜色,内存占用量是96位即12字节;而用24位整数表示法编码,只要一个int类型变量,占用4个字节(高8位空着,低24位用于表示颜色);用16位整数表示法编码,只要一个short类型变量,占2个字节;因此可以看出采用整数表示法编码颜色值,可以大大节省内存,当然,颜色质量也会相对低一些。在Android中获取Bitmap的时候一般也采用整型编码。

以上2种整型编码的表示法中,R、G、B各分量的顺序可以是RGB或BGR,Android里采用的是RGB的顺序,本文也都是遵循此顺序来讨论。在24位整型表示法中,由于R、G、B分量各占8位,有时候业内也以RGB888来指代这一信息;类似的,在16位整型表示法中,R、G、B分量分别占5、6、5位,就以RGB565来指代这一信息。

现在再考虑有透明度的颜色编码,其实方式与无透明度的编码方式一样:24位整型编码RGB模型采用int类型变量,其闲置的高8位正好用于放置透明度分量,其中0表示全透明,255表示完全不透明;按照A、R、G、B的顺序,就可以以ARGB8888来概括这一情形;而16位整型编码的RGB模型采用short类型变量,调整各分量所占为数分别至4位,那么正好可以空出4位来编码透明度值;按照A、R、G、B的顺序,就可以以ARGB4444来概括这一情形。回想一下Android的BitmapConfig类中,有ARGB_8888、ARGB_4444、RGB565等常量,现在可以知道它们分别代表了什么含义。同时也可以计算一张图片在内存中可能占用的大小,比如采用ARGB_8888编码载入一张1920*1200的图片,大概就会占用1920*1200*4/1024/1024=8.79MB的内存。

3.Bitmap在内存中的存储区域

http://www.7dot9.com/2010/08/android-bitmap内存限制/ 一文中对Android内存限制问题做了一些探讨,作者认为Bitmap对象通过栈上的引用来指向堆上的Bitmap对象,而Bitmap对象又对应了一个使用了外部存储的native图像,实际上使用的是byte[]来存储的内存空间。但为了确保外部分配内存成功,应该保证当前已分配的内存加上当前需要分配的内存值,大小不能超过当前堆的最大内存值,而且内存管理上将外部内存完全当成了当前堆的一部分。

4.Java对象的引用类型

(1)强引用(StrongReference)如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

(2)软引用(SoftReference)如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

(3)弱引用(WeakReference)弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

(4)虚引用(PhantomReference)“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

解决OOM的常用方案

内存限制是Android对应用的一个系统级限制,作为应用层开发人员,没有办法彻底去消灭这个限制,但是可以通过一些手段去合理使用内存,从而规避这个问题。以下是个人总结的一些常用方法:

(1)缓存图像到内存,采用软引用缓存到内存,而不是在每次使用的时候都从新加载到内存;

(2)调整图像大小,手机屏幕尺寸有限,分配给图像的显示区域本身就更小,有时图像大小可以做适当调整;

(3)采用低内存占用量的编码方式,比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存;

(4)及时回收图像,如果引用了大量Bitmap对象,而应用又不需要同时显示所有图片,可以将暂时用不到的Bitmap对象及时回收掉;

(5)自定义堆内存分配大小,优化Dalvik虚拟机的堆内存分配;

本文主要将对前面4种方式做演示和分析。

演示试验说明

为了说明出现OOM的场景和解决OOM的方法,本人制作了一个Android应用——OomDemo来演示,此应用的基本情况说明如下:

(1)该应用展示一个gallery,该gallery只加载图片,gallery的adapter中传入图片的路径而不是图片对象本身,adapter动态加载图片;

(2)演示所用的图片预存储到sdcard的cache目录下,文件名分别为a.jpg,b.jpg…r.jpg,总共18张;

(3)图片为规格1920*1200的jpg图片,文件大小在423KB-1.48MB范围内;

(4)运行环境:模拟器——android2.2版本系统——480*320屏幕尺寸;Moto Defy——2.3.4版本CM7系统——854*480屏幕尺寸;

(5)程序基本结构图:

演示结果与说明

1.演示一

首先采用最简单的图片加载方式,不带任何图片缓存、调整大小或者回收,SimpleImageLoader.class便是承担此职责。加载图片部分的代码如下:

@Override
public Bitmap loadBitmapImage(String path) {
     
}
@Override
public Drawable loadDrawableImage(String path) {
     
}

演示结果:在模拟器上图片只能加载1-3张,之后便会出现OOM错误;在Defy上不会出现错误;原因是两者内存限制不同,Defy上运行的是第三方ROM,内存分配有40MB。另外gallery每次显示一张图片时,都要重新解析获得一张图片,尽管在Defy上还未曾出错,但当图片量加大,GC回收不及时时,还是有可能出现OOM。

2.演示二

为图片加载的添加一个软引用缓存,每次图片从缓存中获取图片对象,若缓存中不存在,才会从Sdcard加载图片,并将该对象加入缓存。同时软引用的对象也有助于GC在内存不足的时候回收它们。ImageLoaderWithCache.class负责这个职责,关键代码如下:

private HashMap> mImageCache;


     

演示结果:在模拟器上,能不无缓存时多加载1-2张图片,但还是会出现OOM;在Defy上不曾出错。由于本次所用的图片都相对比较占内存,在GC还未来得及回收软引用对象时,就又要申请超出剩余量的内存空间,因此仍然没能完全避免OOM。如果换成加载大量的小图片,比如100*100规格的,缓存中软引用的作用可能就发挥出来了。(这一假设可以进一步试验证明一下)

3.演示三

为了进一步避免OOM,除了缓存,还可以对图片进行压缩,进一步节省内存,多数情况下调整图片大小并不会影响应用的表现力。ImageLoaderWithScale.class便是负责这个职责,调整大小的代码如下:

BitmapFactory.Options options = new BitmapFactory.Options();

     

演示结果:在上述代码中,首先解码图片的边界,在不需要得到Bitmap对象的前提下就能获得图像宽高(宽高值分别被设置到options.outWidth和options.outHeight两个属性中)。computeSampleSize这个方法的参数分别为“解析图片所需的BitmapFactory.Options”、“调整后图片最小的宽或高值”、“调整后图片的内存占用量上限”。结合原始图片的宽高,此方法可以计算得到一个调整比例,再用此比例调整原始图片并加载到内存中,此时图片所消耗的内存不会超出事先指定的大小。在模拟器中,限制图片所占内存大小为1*1024*1024时,比未压缩过时能加载更多图片,但仍然会出现OOM;若限制图片所占内存大小为0.5*1024*1024,则能完整的载入所有图片。所以调整图片大小还是能够有效节省内存的。在Defy中不会出错,原因同上。

4.演示四

在有些情况下,严重缩小图片还是会影响应用的显示效果的,所以有必要在尽可能少地缩小图片的前提下展示图片,此时手动去回收图片就变得尤为重要。在类ImageLoaderWithRecyle.class中,便增加了回收图片资源的方法:

@Override

     

    

     

演示结果:图片压缩限制仍然维持在1*1024*1024,在adapter中,及时调用releaseImage方法,回收暂时不需要的图片。此时模拟器中也从未出现过OOM,所以总的来讲,综合缓存、调整大小、回收等各种手段,还是能够有效避免OOM的。

小结

本文介绍了软引用缓存、调整大小、回收等手段来避免OOM,总体来说效果还是明显的。但实际应用场景中,图片的应用不想本文所演示的那样简单,有时候图片资源可能来自与网络,这时需要配合异步加载的方式先下载图片并通过回调的方法来显示;有时候图片资源还需要加边框、加文字等额外修饰,所以在图片加载之后还要另做处理。

另外由于本人能力所限以及时间关系,本文还有诸多不完善之处。比如对Android内存分配的理解不深,没能透彻地解释Bitmap的内存占用情况;通过自定义堆内存分配大小,优化Dalvik虚拟机的堆内存分配的方法来解决OOM,本文也没有给予演示;再比如在上文的演示试验里,没有把内存占用情况的详细信息用图像形式直观地展示出来;还有演示所用的图片数量过少、规格单一、测试环境偏少,所有没能进行更加严谨科学的对比试验,遗漏了某些意外情况。最后欢迎大家来共同探索、交流并提出建议。

标签:OOM,加载,回收,内存,Android,图片,小结,引用
From: https://blog.51cto.com/u_548275/6885675

相关文章

  • Android之Widget  Frame…
        2AppWidget Framework2.1定义    (1)AppWidgetService是框架的的核心类,是系统service之一,它负责widgets的管理工作。加载,删除,定时事件等都需要AppWidgetService的处理。开机自启动的。     ......
  • 差分小结
    应用区间的修改(对区间内所有元素做相同的修改)和询问时间复杂度修改一次区间O(1)一维差分D[i]为差分数组,递推公式为a[i]=a[i-1]+D[i]区间修改需要D[L]+=n,D[R+1]-=n;二维差分递推公式为a[i][j]=D[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1]也可以直接累加for(inti=1;i<=n;......
  • android网络通信之HTTP协议教程实…
    在现在的开发和应用中,网络通讯是必不可少的。虽然还是比较怀念小时候,抱着一台95在那里玩单机游戏玩的天昏地暗的时光,但是,现在,就算一个幼儿园的小盆友如果问你要手机玩游戏,突然发现居然买不了冰激凌草莓果汁什么的去喂talkinggina,或者切出一个超爆的水果分数却传不到网上去炫......
  • Android AIDL——实现机制浅析
    1.基于前面写的aidl使用,这段时间准备研究ActivityManager框架,对aidl进行了更深入的研究,因为android框架大量使用了进程通信机制,所以,在研究androidframework前认真研究一下AIDL的实现机制十分有必要的3.aidl接口packagecom.cao.android.demos.binder.aidl;importcom.cao.an......
  • Android NFC简介
    目录:AndroidNFC1 简介1.1 什么是 NFC1.2Android 上的 NFC1.3Android 中的 NFC 通讯机制2 用法2.1 读取 tag2.2 发送消息2.2.1 发送过程2.2.2NDEF 消息格式1 简介1.1 什么是 NFC近场通讯 (NearFiledCommunication) 技术是一种近距离的高频无线......
  • Android-组件属性
    ImageView:app:tint=“”(tint:染色)作用:将非透明像素点染成指定颜色 EditText:android:selectAllOnFocus="true"作用:输入框获取焦点后,获取到所有文本内容 ......
  • AndroidAnnotations 自定义控件 ant编译 找不到类
    问题:今天使用jenkins+ant编译android项目,一直编译不通过,报错内容就是,自定义控件找不到类。确认了几次svn提交,项目代码完全提交,所以想到是不是代码的问题。故回头查询代码。在代码中使用了下划线类。这个类是在AA中自动生成的,而报错的地方,正是编译生成的地方,觉得可能和编......
  • Android从相册、照相机选择图片(支持4.4+,KIKAT以上)
    开发都会遇到从手机的相册和照相机选择图片的功能,下面是一段通用的代码,用来选择手机中的图片,以后用的时候,直接调用就可以了。使用到的类属性publicfinalstaticintCONSULT_DOC_PICTURE=1000;publicfinalstaticintCONSULT_DOC_CAMERA=1001;privateintSELECT_PICTURE......
  • AndroidAnnotations 使用在library项目中出现无法找到R文件下资源索引的错误
    AndroidAnnotations使用在library项目中出现无法找到R文件下资源索引的错误使用AndroidAnnotations(以下简称AA)已经有一段时间了,挺好用的,基本上完全可以满足Android开发需要,可以简化代码,所以现在在岗编写的所有的项目都是基于AA实现的。AA现在最新版本为3.2官方地址:http://android......
  • Kotlin 踩坑日记(六)Android studio 3.0.0 Canary 4 Instant Run
    前言宁夏的特色假日,开斋节放了5天,今天回来上班。放假前,Androidstudio3.0发布了Canary4的升级,因为即将放假,所以就没有升级,节后上班第一天,就安排升级并且试用一下。DebugApk找不到Application类没有别的问题,只是在InstantRun环境下,找不到App的Application类,应用安装成功后,......