首页 > 技术文章 > Android开源项目 Universal imageloader 源码研究之Lru算法

gaofengworking 2015-12-29 15:03 原文

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的,但是没有把内存大小算进去。

 

推荐阅读