在上一篇博文Android Bitmap内存限制中我们详细的了解并分析了Android为什么会在Decode Bitmap的时候出现OOM错误,简单的讲就是Android在解码图片的时候使用了本地代码来完成解码的操作,但是使用的内存是堆里面的内存,而堆内存的大小是收VM实例可用内存大小的限制的,所以当应用程序可用内存已经无法再满足解码的需要时,Android将抛出OOM错误。 这里讲一个题外话,也就是为何Android要限制每个应用程序的可用内存大小呢?其实这个问题可能有多方面的解答,目前我自己考虑到的有两点: 使得内存的使用更为合理,限制每个应用的可用内存上限,可以防止某些应用程序恶意或者无意使用过多的内存,而导致其他应用无法正常运行,我们众所周知的Android是有多进程的,如果一个进程(也就是一个应用)耗费过多的内存,其他的应用还搞毛呢?当然在这里其实是有一个例外,那就是如果你的应用使用了很多本地代码,在本地代码中创建对象解码图像是不会被计算到的,这是因为你使用本地方法创建的对象或者解码的图像使用的是本地堆的内存,跟系统是平级的,而我们通过Framework调用BitmapFactory.decodeFile()方法解码时,系统虽然也是调用本地代码来进行解码的,但是Android Framework在实现的时候,刻意地将这部分解码使用的内存从堆里面分配了而不是从本地堆里分配的内存,所以才会出现OOM,当然并不是说从本地堆里分配就不会出现OOM,本地堆分配内存超过系统可用内存限制的话,通常都是直接崩溃,什么错误可能都看不到,也许会有一些崩溃的错误字节码之类的。 省电的考虑,呃…,原因我好像也不能很明白地说出来。 回到正题来,我们在应用的设计和开发中可能会经常碰到需要在一个界面上显示数十张图片乃至上百张,当然限于手机屏幕的大小我们通常在设计中会使用类似于列表或者网格的控件来展示,也就是说通常一次需要显示出来图片数还是一个相对确定的数字,通常也不会太大。如果数目比较大的画,通常显示的控件自身尺寸就会比较小,这个时候可以采用缩略图策略。下面我们来看看如果避免出现OOM的错误,这个解决方案参考了Android示范程序XML Adapters中的ImageDownloader.java中的实现,主要是使用了一个二级缓存类似的机制,就是有一个数据结构中直接持有解码成功的Bitmap对象引用,同时使用一个二级缓存数据结构持有解码成功的Bitmap对象的SoftReference对象,由于SoftReference对象的特殊性,系统会在需要内存的时候首先将SoftReference对象持有的对象释放掉,也就是说当VM发现可用内存比较少了需要触发GC的时候,就会优先将二级缓存中的Bitmap回收,而保有一级缓存中的Bitmap对象用于显示。 其实这个解决方案最为关键的一点是使用了一个比较合适的数据结构,那就是LinkedHashMap类型来进行一级缓存Bitmap的容器,由于LinkedHashMap的特殊性,我们可以控制其内部存储对象的个数并且将不再使用的对象从容器中移除,这就给二级缓存提供了可能性,我们可以在一级缓存中一直保存最近被访问到的Bitmap对象,而已经被访问过的图片在LinkedHashMap的容量超过我们预设值时将会把容器中存在时间最长的对象移除,这个时候我们可以将被移除出LinkedHashMap中的对象存放至二级缓存容器中,而二级缓存中对象的管理就交给系统来做了,当系统需要GC时就会首先回收二级缓存容器中的Bitmap对象了。在获取对象的时候先从一级缓存容器中查找,如果有对应对象并可用直接返回,如果没有的话从二级缓存中查找对应的SoftReference对象,判断SoftReference对象持有的Bitmap是否可用,可用直接返回,否则返回空。 主要的代码段如下: private static final int HARD_CACHE_CAPACITY = 16; // Hard cache, with a fixed maximum capacity and a life duration private static final HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) { private static final long serialVersionUID = -57738079457331894L; @Override [...]
Category Archives: Java
Android上如何正确实现程序的联网,事关WIFI/CMWAP/CMNET
2010年07月19日 – 11:54 上午
Android程序正常使用APN登录网络,主要讲述了如何在复杂的网络环境下,通过程序来控制使用可用的网络
例如针对WIFI/CMWAP/CMNET等进行单独的处理,保证网络的可用性。
LineNumberReader和FileWriter同时使用碰到的问题
2010年01月27日 – 11:08 下午
关于LineNumberReader和FileWriter同时使用碰到的问题的解决方法,和相关注意事项
关于JTree的一些碎碎念
2010年01月16日 – 10:39 下午
简单描述了一些JTree的开发技巧,主要有TreeCellRenderer和TreeCellEditor的一些接口的使用方法,并且如何与JTree结合使用的技巧。
