天天看点

bug修复---印象最深的OOM

因为android系统的app的每个进程或者每个虚拟机有个最大内存限制,如果申请的内存资源超过这个限制,系统就会抛出OOM错误。跟整个设备的剩余内存没太大关系。比如比较早的android系统的一个虚拟机最多16M内存,当一个app启动后,虚拟机不停的申请内存资源来装载图片,当超过内存上限时就出现OOM。

常见避免OOM的几个注意点:

适当调整图像大小 。因为手机屏幕尺寸有限,分配给图像的显示区域有限,尤其对于超大图片,加载自网络或者sd卡,图片文件提及达到几M或者十几个M的。加载到内存前,先算出该bitmap的大小,然后通过适当调节采样率使得加载的图片刚好,或稍大捷克在手机屏幕上显示就满意了:

BimtapFactory.Option opts = new BitampFactory.Option();

opts.inJustDecodeBounds =  true ;
    opts.inSampleSize=computeSample(opts, minSideLength, maxNumOfPixels);  // Android 提供了一种动态计算的方法 computeSampleSize
    opts.inJustDecodeBounds =  false ;
     try {
            return  BitmapFactory.decodeFile(imageFile, opts);
    }  catch (OutOfMemoryError err){           

}

图像缓存:在listview或Gallery等控件中一次性加载大量图片时,只加载屏幕显示的资源,尚未显示的不加载,移出屏幕的资源及时释放,采用强引用+软引用2级缓存,提高加载性能。缓存图像到内存,采用软引用缓存到内存,而不是在每次使用的时候都从新加载到内存。

采用低内存占用量的编码方式:比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存。

及时回收图像:如果引用了大量的Bitmap对象,而应用又不需要同时显示所有图片。可以将暂时不用到的Bitmap对象及时回收掉。对于一些明确直到图片使用情况的场景可以主动recycle回收。App的启动splash画面上的图片资源,使用完就recycle。对于帧动画,可以加载一张,画一张,释放一张。

不要在循环中创建过多的本地变量:慎用static,用static来修饰成员变量时,该变量就属于该类,而不是该类实例,它的生命周期是很长的。如果用它来引用一些内存占用太多的实例,这时候就要谨慎对待了。

App使用图片时避免OOM的几种方式:

对于app里使用的大量图片,采用方式:使用时加载,不显示时直接置null或recycle。

这样处理是个好习惯,记本可以杜绝OOM,但是缺憾是代码多了,可能会忘记某些资源recycle。

而有些情况下会出现特定图片反复加载,释放,再加载等,低效率的事情。

建个SoftReference的hashmap

使用图片时先查询这个hashmap是否有softreference,softreference里的图片是否为空,

如果为空就加载图片到softreference并加入hashmap。无需再代码里显式的处理图片的回收与释放,gc会自动处理资源的释放。这种方式处理起来简单实用,能一定程度上避免前一种方法反复加载释放的低效率。但还不够优化。

Android示范程序ImageDownloader.java, 使用了一个二级缓存机制。就是有一个数据结构直接持有解码成功的Bitmap对象引用,同时使用一个二级缓存数据结构保持淘汰的Bitmap的softreference对象,由于softreference对象的特殊性,系统会再需要内存的时候首先将softreference持有的对象释放掉,也就是说当vm发现可用的内存较少需要出发gc的时候,二级缓存中的bitmap对象将被回收,而持有一级缓存的bitmap对象用于显示。其实这个解决方案最为关键的一点是使用了一个比较合适的数据结构,那就是LinkedHashMap类型来进行一级缓存Bitmap的容器。由于LinkeHashMap的特殊性,我们可以控制其内存存储对象的个数并且将不在使用的对象从容器中移除,放到softreference二级缓存里,我们可以在一级缓存中一致保存最近被访问到的bitmap对象,而已经被访问过的图片在LinkedHashMap的容量超过我们预设值时将会把容器中存在的时间最长的对象移除,这个时候我么可以将被移除的LinkedHashMap中的放到二级缓存容器,而二级缓存中的对象管理就交给系统来做了,当系统需要gc时就会首先回收二级缓存容器的Bitmap对象了。

在获取图片对象时候先从一级缓存容器中查找,如果有对应对象并可用直接返回,如果没有的话从二级缓存中查找对应的SoftReference, 判断SoftReference对象持有的Bitmap是否可用,可用直接返回,否则返回空。如果二级缓存都找不到图片,那就直接加载图片资源

继续阅读