LruCache的原理和使用

为什么使用LruCache缓存

首先看做一个缓存的关键步骤, 存储,读取,清理。
存储和读取区别不是很大,Lru算法做缓存和普通的数据结构做缓存相比最大的不同就是对清理策略的处理,由于空间的限制,所有的缓存都需要一个清理策略,最简单的清理策略就是定一个过期时间和一个最大空间,超过最大占用空间了, 需要进入清理流程, 还有超过过期时间的缓存内容被标记为过期,进入清理流程,所以我们需要维护一个时间字段,和一个空间最大值,当然也可以实现比较完美的缓存。
Lru缓存简单的地方在于,他只需要一个最大空间值, 而不需要自己维护过期时间,通过一些数据结构,巧妙的让每次缓存命中或者新加入的数据,排在最前面, 而后面的数据就沉淀为长时间没有使用过的数据,简单是简单了,但是可定制化和灵活性肯定是比较差的了,就比如说这个空间最大值,maxSize定多少就很关键,如果太小了,而数据量比较大,那么这个lru算法就失去意义了, 因为可能被删掉的数据也是不久前访问过的,如果maxSize定的太大了, 对空间会造成浪费,个人认为肯定是比普通的做法最大空间小,因为普通做法出发最大空间时候,一定是已经占用很多空间了,否则使用时间来管理即可满足条件。

LruCache对于热点缓存优势比较大, 一部分频繁多次使用的数据, 效率很高,例如图片缓存,一些首页之类的图片肯定是频繁的,次级页面的优先级就不高了这种场景就比较合适,而对于,偶发的散列的,周期性的数据明显是不合适的。

android sdk lrucache原理

主要使用了LinkedHashMap这个数据结构,他自己就可以制定使用访问顺序存储数据,每次put和get数据的时候,会把新数据放到双向链表的尾端,方便读取,不常用的会逐渐沉淀到头部。

 public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        V previous;
        synchronized (this) {
			//put次数,可以通过get方法获取。
            putCount++;
            //size是计算一共现有多少单位的,通过sizeOf(key,value)方法得到的, 如果不重写的话会加1。
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if (previous != null) {
            	//如果之前有值,需要减掉他的size
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
        	//可以重写方法,自定义之前有值情况的处理办法,默认是个空实现。
            entryRemoved(false, key, previous, value);
        }
		//计算是否触发清理条件
        trimToSize(maxSize);
        return previous;
    }
 private int safeSizeOf(K key, V value) {
 		//真正的实现在sizeOf
        int result = sizeOf(key, value);
        if (result < 0) {
            throw new IllegalStateException("Negative size: " + key + "=" + value);
        }
        return result;
    }

    /**
     * 根据key value来判断这次缓存的size,如果不重写,每一次都是1
     */
    protected int sizeOf(K key, V value) {
        return 1;
    }
     /**
     * 获取缓存的size
     */
    public synchronized final int size() {
        return size;
    }
public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
	            //get次数,可以通过get方法获取。
                hitCount++;
                return mapValue;
            }
            missCount++;
        }
		
        /* 如果取到了上面就返回了,如果没取到进入下面的逻辑~
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         * create方法默认空实现,可以重写来自定义当取不到的时候还想插入的逻辑。
         */

        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }

        synchronized (this) {
            createCount++;
            //自己实现create的话,通过key取不到,那么就会插入key和create的返回值
            mapValue = map.put(key, createdValue);
			//因为create方法调用在同步区域之外,所以有可能其他线程已经put了key的值,此时,优先使用其他线程put的值
            if (mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue);
            } else {
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
        	//自定义处理有值的情况和get相同
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
        	//计算是否进入清理流程
            trimToSize(maxSize);
            return createdValue;
        }
    }
    
    protected V create(K key) {
        return null;
    }
 //如果超过最大值maxSize被清理掉
 private void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }

                if (size <= maxSize) {
                    break;
                }

                // BEGIN LAYOUTLIB CHANGE
                // get the last item in the linked list.
                // This is not efficient, the goal here is to minimize the changes
                // compared to the platform version.
                Map.Entry<K, V> toEvict = null;
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }
                // END LAYOUTLIB CHANGE

                if (toEvict == null) {
                    break;
                }

                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

你可能感兴趣的:(android,技术,code)