首页 > 代码库 > LruCache 缓存

LruCache 缓存

内存缓存(LruCache):
系统提供的LruCache类是非常适合用作缓存Bitmap任务的,
它将最近被引用到的对象存储在一个强引用的LinkedHashMap中,并且在缓存超过了指定大小之后将最近不常使用的对象释放掉。
注意:以前有一个非常流行的内存缓存实现是SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,
然而现在已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,这使得上述的方案相当无效。
http://developer.android.com/reference/android/util/LruCache.html

 

/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package android.util;import java.util.LinkedHashMap;import java.util.Map;/** * Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。 * 当cache已满的时候加入新的item时,在队列尾部的item会被回收。 * 如果你cache的某个值需要明确释放,重写entryRemoved() * 如果key相对应的item丢掉啦,重写create().这简化了调用代码,即使丢失了也总会返回 * 默认cache大小是测量的item的数量,重写sizeof计算不同item的 *  大小。 *   int cacheSize = 4 * 1024 * 1024; // 4MiB  *   LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {  *       protected int sizeOf(String key, Bitmap value) {  *           return value.getByteCount();  *       }  *   }} *   不允许key或者value为null *   当get(),put(),remove()返回值为null时,key相应的项不在cache中 */public class LruCache<K, V> {    private final LinkedHashMap<K, V> map;    /** Size of this cache in units. Not necessarily the number of elements. */    private int size; //已经存储的大小    private int maxSize; //规定的最大存储空间    private int putCount; //put的次数    private int createCount;//create的次数    private int evictionCount;//回收的次数    private int hitCount;//命中的次数    private int missCount;//丢失的次数    /**     * @param maxSize for caches that do not override {@link #sizeOf}, this is     *     the maximum number of entries in the cache. For all other caches,     *     this is the maximum sum of the sizes of the entries in this cache.     */    public LruCache(int maxSize) {        if (maxSize <= 0) {            throw new IllegalArgumentException("maxSize <= 0");        }        this.maxSize = maxSize;        this.map = new LinkedHashMap<K, V>(0, 0.75f, true); // LinkedHashMap的初始化放在构造器中,                                                            //这里将LinkedHashMap的accessOrder设置为true,为插入顺序,默认是访问顺序    }    /**     * Sets the size of the cache.     * @param maxSize The new maximum size.     *     * @hide     */    public void resize(int maxSize) {        if (maxSize <= 0) {            throw new IllegalArgumentException("maxSize <= 0");        }        synchronized (this) {            this.maxSize = maxSize;        }        trimToSize(maxSize);    }    /**     * Returns the value for {@code key} if it exists in the cache or can be     * created by {@code #create}. If a value was returned, it is moved to the     * head of the queue. This returns null if a value is not cached and cannot     * be created.     * 通过key返回相应的item,或者创建返回相应的item。相应的item会移动到队列的头部,     * 如果item的value没有被cache或者不能被创建,则返回null。     */    public final V get(K key) {        if (key == null) { //不允许空键              throw new NullPointerException("key == null");        }        V mapValue;        synchronized (this) {            mapValue = map.get(key);//调用LinkedHashMap的get方法              if (mapValue != null) {                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.         * 如果丢失了就试图创建一个item         */        V createdValue = create(key);        if (createdValue =http://www.mamicode.com/= null) {            return null;        }        synchronized (this) {            createCount++; //如果创建成功,那么create次数加1              mapValue =http://www.mamicode.com/ map.put(key, createdValue);            if (mapValue != null) {                // There was a conflict so undo that last put                map.put(key, mapValue);            } else {                size += safeSizeOf(key, createdValue);            }        }        if (mapValue != null) {            entryRemoved(false, key, createdValue, mapValue);            return mapValue;        } else {            trimToSize(maxSize);            return createdValue;        }    }    /**     * Caches {@code value} for {@code key}. The value is moved to the head of     * the queue.     *     * @return the previous value mapped by {@code key}.     */    public final V put(K key, V value) {        if (key == null || value =http://www.mamicode.com/= null) { //键值不允许为空              throw new NullPointerException("key == null || value =http://www.mamicode.com/= null");        }        V previous;        synchronized (this) {            putCount++;            size += safeSizeOf(key, value);//统计放入的值的大小,然后增加size的记录值              previous = map.put(key, value);//把新值放入缓存map中,然后获得旧值              if (previous != null) { //旧值不为空,由于替换了旧值,所以需要把缓存数据总大小减去这个旧值的大小                    size -= safeSizeOf(key, previous);            }        }        //还要调用entryRemoved()方法来让子类去处理不用的旧值previous,可能按照自己的方式去释放它。          //当然了,子类也可以不实现这个方法。          if (previous != null) {            entryRemoved(false, key, previous, value);        }        trimToSize(maxSize);        return previous;    }    /**     * Remove the eldest entries until the total of remaining entries is at or     * below the requested size.     *     * @param maxSize the maximum size of the cache before returning. May be -1     *            to evict even 0-sized elements.     把最少访问的老数据删除,直到总数据大小在上限的范围之内.      数据上限. 值可能是-1,这样就会删除所有的缓存数据.     */    public void trimToSize(int maxSize) {        while (true) { //不断删除linkedHashMap头部entry,也就是最近最少访问的条目,直到size小于最大容量              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;                }                Map.Entry<K, V> toEvict = map.eldest(); //获取最少访问的entry                if (toEvict == null) {                    break;                }                key = toEvict.getKey();                value = toEvict.getValue();                map.remove(key);//删除最少访问的entry                  size -= safeSizeOf(key, value);                evictionCount++;            }            entryRemoved(true, key, value, null);        }    }    /**     * Removes the entry for {@code key} if it exists.     *     * @return the previous value mapped by {@code key}.     */    public final V remove(K key) {        if (key == null) {            throw new NullPointerException("key == null");        }        V previous;        synchronized (this) {            previous = map.remove(key);//调用LinkedHashMap的remove方法              if (previous != null) {                size -= safeSizeOf(key, previous);            }        }        if (previous != null) {            entryRemoved(false, key, previous, null);        }        return previous;    }    /**     * Called for entries that have been evicted or removed. This method is     * invoked when a value is evicted to make space, removed by a call to     * {@link #remove}, or replaced by a call to {@link #put}. The default     * implementation does nothing.     *     * <p>The method is called without synchronization: other threads may     * access the cache while this method is executing.     *     * @param evicted true if the entry is being removed to make space, false     *     if the removal was caused by a {@link #put} or {@link #remove}.     * @param newValue the new value for {@code key}, if it exists. If non-null,     *     this removal was caused by a {@link #put}. Otherwise it was caused by     *     an eviction or a {@link #remove}.     */    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}    /**     * Called after a cache miss to compute a value for the corresponding key.     * Returns the computed value or null if no value can be computed. The     * default implementation returns null.     *     * <p>The method is called without synchronization: other threads may     * access the cache while this method is executing.     *     * <p>If a value for {@code key} exists in the cache when this method     * returns, the created value will be released with {@link #entryRemoved}     * and discarded. This can occur when multiple threads request the same key     * at the same time (causing multiple values to be created), or when one     * thread calls {@link #put} while another is creating a value for the same     * key.     */    protected V create(K key) {        return null;    }    private int safeSizeOf(K key, V value) {        int result = sizeOf(key, value);        if (result < 0) {            throw new IllegalStateException("Negative size: " + key + "=" + value);        }        return result;    }    /**     * Returns the size of the entry for {@code key} and {@code value} in     * user-defined units.  The default implementation returns 1 so that size     * is the number of entries and max size is the maximum number of entries.     *     * <p>An entry‘s size must not change while it is in the cache.     * 这个方法用于计算每个条目的大小,子类必须得复写这个类。     */    protected int sizeOf(K key, V value) {        return 1;    }    /**     * Clear the cache, calling {@link #entryRemoved} on each removed entry.     */    public final void evictAll() {        trimToSize(-1); // -1 will evict 0-sized elements    }    /**     * For caches that do not override {@link #sizeOf}, this returns the number     * of entries in the cache. For all other caches, this returns the sum of     * the sizes of the entries in this cache.     */    public synchronized final int size() {        return size;    }    /**     * For caches that do not override {@link #sizeOf}, this returns the maximum     * number of entries in the cache. For all other caches, this returns the     * maximum sum of the sizes of the entries in this cache.     */    public synchronized final int maxSize() {        return maxSize;    }    /**     * Returns the number of times {@link #get} returned a value that was     * already present in the cache.     */    public synchronized final int hitCount() {        return hitCount;    }    /**     * Returns the number of times {@link #get} returned null or required a new     * value to be created.     */    public synchronized final int missCount() {        return missCount;    }    /**     * Returns the number of times {@link #create(Object)} returned a value.     */    public synchronized final int createCount() {        return createCount;    }    /**     * Returns the number of times {@link #put} was called.     */    public synchronized final int putCount() {        return putCount;    }    /**     * Returns the number of values that have been evicted.     */    public synchronized final int evictionCount() {        return evictionCount;    }    /**     * Returns a copy of the current contents of the cache, ordered from least     * recently accessed to most recently accessed.     */    public synchronized final Map<K, V> snapshot() {        return new LinkedHashMap<K, V>(map);    }    @Override public synchronized final String toString() {        int accesses = hitCount + missCount;        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;        return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",                maxSize, hitCount, missCount, hitPercent);    }}

总结:
1.LruCache封装了LinkedHashMap,提供了LRU缓存的功能;
2.LruCache通过trimToSize方法自动删除最近最少访问的键值对;
3.LruCache不允许空键值;
4.LruCache线程安全;
5.继承LruCache时,必须要复写sizeof方法,用于计算每个条目的大小。

LruCache 缓存