https://github.com/nostra13/Android-Universal-Image-Loader
universal imageloader 源码研究之Lru算法
LRU - Least Recently Used 最近最少使用算法
字面意思就是在数据队列里面 最少使用的优先移除,腾出空间 提高任务调度,
接口
com.nostra13.universalimageloader.cache.memory.MemoryCache
实现
com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache
上面的是内存中最近最少使用的数据 将会移除从内存中。
通过 com.nostra13.universalimageloader.core.ImageLoaderConfiguration 可以设置cacheSize
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(context) .memoryCacheSize(10 * 1024 * 1024) // 10MB 大小 .build(); ImageLoader imageloader = ImageLoader.getInstance(); imageloader.init(configuration);
如果客户端没有主动设置cacheSize 将会创建一个默认的大小
com.nostra13.universalimageloader.core.DefaultConfigurationFactory
public static MemoryCache createMemoryCache(Context context, int memoryCacheSize) { if (memoryCacheSize == 0) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); int memoryClass = am.getMemoryClass(); //这个返回一个APK的heap最大值限制 if (hasHoneycomb() && isLargeHeap(context)) { memoryClass = getLargeMemoryClass(am); } memoryCacheSize = 1024 * 1024 * memoryClass / 8; // 取 1/8 大小内存, 如果是128MB cacheSize为16MB大小 } return new LruMemoryCache(memoryCacheSize); }
// am.getMemoryClass(); 我的PAD打印是128MB static public int staticGetMemoryClass() { // Really brain dead right now -- just take this from the configured // vm heap size, and assume it is in megabytes and thus ends with "m". String vmHeapSize = SystemProperties.get("dalvik.vm.heapgrowthlimit", ""); if (vmHeapSize != null && !"".equals(vmHeapSize)) { return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1)); } return staticGetLargeMemoryClass(); }
看看 LruMemoryCache
public LruMemoryCache(int maxSize) { //初始化大小 if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true); //LinkedHashMap 数据结构 //注意 第三个参数为true表示 每次执行put和get会把对应的Entry移到最新位置,如果设置为false 位置不会变化 会导致移除的对象为第一个put的对象 /** * 比如顺序: put(01,bitmap), put(02,bitmap) put(03,bitmap) put(01,bitmap) * 如果设置为true会移除掉 key=02的数据 * 如果设置为false会移除掉key=01的数据,但是01的数据 最后又更新了,显然不符合LRU的特点 * 如果调用了get方法 也会把item位置更新到最新,这样没有被调用的数据 就排在最后 可能会被移除掉 * MAP 迭代的话 第一个元素就是 最少使用的对象 */ }
public final boolean put(String key, Bitmap value) { if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } synchronized (this) { size += sizeOf(key, value); //开始计算大小 Bitmap previous = map.put(key, value); //之前有重复的key 取出对象 ,put函数返回上次已经存储key对应的值 if (previous != null) { size -= sizeOf(key, previous); //减去以前的对象大小,因为已经被新的value覆盖了 } } trimToSize(maxSize); //尝试移除工作 return true; }
private void trimToSize(int maxSize) { while (true) { String key; Bitmap value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (size <= maxSize || map.isEmpty()) { //如果中大小没有超过cacheSize 不需要移除任何对象 break; } Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next(); //取最少使用的Item数据 if (toEvict == null) { break; } key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); //移除最近最少使用的 size -= sizeOf(key, value); //更新内存中的 size 大小 } } }
移除所有
trimToSize(-1) 这样就OK了。
以前用2个HashMap来实现过LRU ,就是其中一个HashMap存储使用最近的时间,只要调用get被使用 就更新一下当前时间。移除对象的时候 根据时间排序移除时间最老的 也是OK的,但是没有把内存大小算进去。