/**
* A cache that holds strong references to a limited number of values. Each time
* a value is accessed, it is moved to the head of a queue. When a value is
* added to a full cache, the value at the end of that queue is evicted and may
* become eligible for garbage collection.
*
* If your cached values hold resources that need to be explicitly released,
* override {@link #entryRemoved}.
*
*
If a cache miss should be computed on demand for the corresponding keys,
* override {@link #create}. This simplifies the calling code, allowing it to
* assume a value will always be returned, even when there's a cache miss.
*
*
By default, the cache size is measured in the number of entries. Override
* {@link #sizeOf} to size the cache in different units. For example, this cache
* is limited to 4MiB of bitmaps:
*
{@code
* int cacheSize = 4 * 1024 * 1024; // 4MiB
* LruCache bitmapCache = new LruCache(cacheSize) {
* protected int sizeOf(String key, Bitmap value) {
* return value.getByteCount();
* }
* }}
*
* This class is thread-safe. Perform multiple cache operations atomically by
* synchronizing on the cache:
{@code
* synchronized (cache) {
* if (cache.get(key) == null) {
* cache.put(key, value);
* }
* }}
*
* This class does not allow null to be used as a key or value. A return
* value of null from {@link #get}, {@link #put} or {@link #remove} is
* unambiguous: the key was not in the cache.
*/
一个高速缓存对有限数量的值持有强引用。每当一个值被访问时,该值被移动到队列头部。当一个值被添加到存满的缓存队列中时,在缓存数据中位于缓存队列最后的数据会被移出或者被回收。
如果缓存值持有的资源需要被释放,需要重写entryRemoved方法
protected void entryRemoved(boolean evi cted, K key, V oldValue, V newValue) {}
如果丢失了一个缓存值,需要根据需求对其对应的键进行计算,需要重写create方法,这样简化了代码的调用,当丢失一个缓存值,仍然能通过计算返回该值,使得总有一个值被返回。
protected V create(K key) {
return null;
}
默认情况下,缓存空间根据条目的数量而定,重写sizeOf方法,根据需求,设置返回不同的缓存空间。
protected int sizeOf(K key, V value) {
return 1;
}
此类不允许使用null作为key或者value,以下 get,put,remove方法在key值为空的情况下允许返回null。
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75f, true);
}
可以看出,在创建LruCache实例的时候需要传入一个maxSize
在实例化过程中,将传入的maxSize的值赋给全局的maxSize,并创建了map的LinkedHashMap对象。由此可见,LruCache是通过LinkedHashMap进行数据存储的。
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}
通过sizOf获取放入的数据需要的空间大小。
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) {
hitCount++;
return mapValue;
}
missCount++;
}
在get方法中,传入了键的值,返回了value值,内部通过map中的get(key)取值,由于V为map的一个泛型,返回时,也是返回get方法中定义的mapValue,所以是存取数据变得方便。
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
在remove方法中,传入了键的值,返回了value值,先通过map中的remove(key)移除。若失败,再通过重写的entryRemoved方法将缓存值持有的资源进行释放。
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;
}
Map.Entry toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
内部是一个无限循环,新数据空间不足以放入时,自动删除最近最少访问的键值对。
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
键、值均不能为空。放入一个数据,并且调用了trimToSize对缓存中的数据进行检测和清理。