• Image-Loader LruMemoryCache


    这段时间在研究Universal-Image-Loader 这个图片处理开源框架,这里主要分析一下它的LRU(Least Resently Used,最近最少使用算法)内存缓存的实现。 
    在UIL它提供的默认缓存类是LruMemoryCache,在它类上面有如下一段注释:

    /**
     * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
     * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may become eligible for garbage collection.<br />
     * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
     */

    说明该缓存存储的是强引用的Bitmap对象,同时每当一个bitmap被访问之后,它就会被放到队列的头部,当队列满了需要删除元素时,就会删除队尾的元素,让它变成可以被垃圾回收器回收的对象。 
    在LruMemoryCache中声明了三个全局变量:

    private final LinkedHashMap<String, Bitmap> map;//存放对象的map容器
    private final int maxSize; //缓存设定的最大值 
    /** Size of this cache in bytes */
    private int size;  //缓存中已经占有的大小

    它的构造数也同样简单:

    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);
        }

    这里传进来的maxSize参数在默认的情况下是程序进程占用内存总数的八分之一,单位是Byte。 
    根据Key值获取对应的bitmap对象,代码超简单:

    /**
         * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
         * of the queue. This returns null if a Bitmap is not cached.
         */
        @Override
        public final Bitmap get(String key) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
    
            synchronized (this) {
                return map.get(key);
            }
        }

    存储bitmap对象:

    @Override
        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); //调用sizeOf这个函数获得该bitmap对象的占用内存的大小,并且让缓存总数增加
                Bitmap previous = map.put(key, value);//这里就是把对象放入容器中的最核心的一句代码,如果容器中已经有了此元素,则返回该元素的value值,否则返回空
                if (previous != null) {
                    size -= sizeOf(key, previous);//如果容器中已经有了此元素,则需要把增加的数量减掉
                }
            }
    
            trimToSize(maxSize); //此函数计算是否超出最大限量,是则删除队尾元素
            return true;
        }

    这里涉及到两个函数: 
    获取图片大小的函数

    private int sizeOf(String key, Bitmap value) {
            return value.getRowBytes() * value.getHeight();
        }

    保证缓存内存量不超过设定的最大值

    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()) {
                        break;
                    }
    
                    Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
                    if (toEvict == null) {
                        break;
                    }
                    key = toEvict.getKey();
                    value = toEvict.getValue();
                    map.remove(key);
                    size -= sizeOf(key, value);
                }
            }
        }

    此外,该类还有一个移除特定key的元素的方法:

    /** Removes the entry for {@code key} if it exists. */
        @Override
        public final Bitmap remove(String key) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
    
            synchronized (this) {
                Bitmap previous = map.remove(key);
                if (previous != null) {
                    size -= sizeOf(key, previous);
                }
                return previous;
            }
        }

    至此整个缓存类就已经解析完了,你会发现其实代码超简单的,其中一个最重要的东西就是存放键值对的容器–LinkedHashMap。LinkedHashMap实现了Map接口,并且具有链表的特性,即有可预知的迭代顺序。通常在默认的情况下,该集合的迭代顺序是按照插入元素的顺序,先插入的元素在队尾,最后插入的在头部;然而当我们调用LinkedHashMap的构造函数LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) ,并传入accessOrder的值为true时,LinkedHashMap就会按照访问先后的顺序迭代,最近被访问的放在队头,最迟访问的在队尾。正是因为这个特性,LinkedHashMap很适合被用来作为LRU缓存的容器。因此有了LinkedHashMap这个神器,我们完全可以仿照UIL的缓存类构建自己的缓存!

  • 相关阅读:
    0xc000000f: Error attempting to read the boot configuration data
    结合使用 Oracle Database 11g 和 Python
    精通 Oracle+Python,第 9 部分:Jython 和 IronPython — 在 Python 中使用 JDBC 和 ODP.NET
    精通 Oracle+Python,第 8 部分:适合 Oracle DBA 使用的 Python
    精通 Oracle+Python,第 6 部分:Python 支持 XML
    精通 Oracle+Python,第 7 部分:面向服务的 Python 架构
    精通 Oracle+Python,第 5 部分:存储过程、Python 编程
    精通 Oracle+Python,第 4 部分:事务和大型对象
    精通 Oracle+Python,第 3 部分:数据解析
    精通 Oracle+Python,第 2 部分:处理时间和日期
  • 原文地址:https://www.cnblogs.com/yaowen/p/6358199.html
Copyright © 2020-2023  润新知