最近做新闻客户端的时候在加载ListView的大图时会发生内存溢出的问题。内存溢出(OOM)。为了便于理解,我们不妨打个比方。缓冲区溢出好比是将十磅的糖放进一个只能装五磅的容器里。一旦该容器放满了,余下的部分就溢出在柜台和地板上,弄得一团糟。由于计算机程序的编写者写了一些编码,但是这些编码没有对目的区域或缓冲区——五磅的容器——做适当的检查,看它们是否够大,能否完全装入新的内容——十磅的糖,结果可能造成缓冲区溢出的产生。如果打算被放进新地方的数据不适合,溢得到处都是,该数据也会制造很多麻烦。但是,如果缓冲区仅仅溢出,这只是一个问题。到此时为止,它还没有破坏性。当糖溢出时,柜台被盖住。可以把糖擦掉或用吸尘器吸走,还柜台本来面貌。与之相对的是,当缓冲区溢出时,过剩的信息覆盖的是计算机内存中以前的内容。除非这些被覆盖的内容被保存或能够恢复,否则就会永远丢失。
首先想到的是三级缓存,从服务器下载下来的图片经过网络缓存,但是这种方法不优先加载, 速度慢,浪费流量。然后是本地缓存,次优先加载, 速度快;最后最好的是内存缓存,优先加载, 速度最快。我们都知道Android默认给每个app只分配16M的内存,所以用软引用 SoftReference 在内存不够时,垃圾回收器会考虑回收。但是在安卓2.3+,系统会优先将SoftReference的对象提前回收掉, 即使内存够用。所以谷歌官方建议用LruCache。least recentlly use 最少最近使用算法
会将内存控制在一定的大小内, 超出最大值时会自动回收, 这个最大值开发者自己定。其实LruCache就是用了很多的HashMap,三百多行的代码。
/** * 内存缓存 */ public class MemoryCacheUtils { // private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new // HashMap<String, SoftReference<Bitmap>>(); private LruCache<String, Bitmap> mMemoryCache; public MemoryCacheUtils() { long maxMemory = Runtime.getRuntime().maxMemory() / 8;// 模拟器默认是16M mMemoryCache = new LruCache<String, Bitmap>((int) maxMemory) { @Override protected int sizeOf(String key, Bitmap value) { int byteCount = value.getRowBytes() * value.getHeight();// 获取图片占用内存大小 return byteCount; } }; } /** * 从内存读 * * @param url */ public Bitmap getBitmapFromMemory(String url) { // SoftReference<Bitmap> softReference = mMemoryCache.get(url); // if (softReference != null) { // Bitmap bitmap = softReference.get(); // return bitmap; // } return mMemoryCache.get(url); } /** * 写内存 * * @param url * @param bitmap */ public void setBitmapToMemory(String url, Bitmap bitmap) { // SoftReference<Bitmap> softReference = new // SoftReference<Bitmap>(bitmap); // mMemoryCache.put(url, softReference); mMemoryCache.put(url, bitmap); } }
java中的引用 - 强引用 垃圾回收器不会回收, java默认引用都是强引用 - 软引用 SoftReference 在内存不够时,垃圾回收器会考虑回收 - 弱引用 WeakReference 在内存不够时,垃圾回收器会优先回收 - 虚引用 PhantomReference 在内存不够时,垃圾回收器最优先回收
</pre><pre name="code" class="java">为进一步优化,我们还可以考虑图片压缩技术:
//图片压缩处理 BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = 2;//宽高都压缩为原来的二分之一, 此参数需要根据图片要展示的大小来确定 option.inPreferredConfig = Bitmap.Config.RGB_565;//设置图片格式
当然,如果我们用BitmapUtils也是可以的。