LruCache源码分析
1. Lrucache封装了LinkedHashMap,并将LinkedHashMap的accessOrder设为true。
可以使遍历顺序和访问顺序一致,其
内部双向链表将会按照近期最少访问到近期最多访问的顺序排列Entry对象
,这可以用来做缓存。
2.LruCache通过trimToSize方法自动删除最近最少访问的键值对;
3.LruCache不允许空键值;
4.LruCache线程安全;
5.继承LruCache时,必须要复写sizeof方法,用于计算每个条目的大小。
/*
* 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.support.v4.util;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Static library version of {@link android.util.LruCache}. Used to write apps
* that run on API levels prior to 12. When running on API level 12 or above,
* this implementation is still used; it does not try to switch to the
* framework's implementation. See the framework SDK documentation for a class
* overview.
*/
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; // 当前的size
private int maxSize; // 最大size
private int putCount; // put的次数
private int createCount; // create的次数
private int evictionCount; // trimToSize中每成功回收一个entry,就会使此值加1
private int hitCount; // hit的次数
private int missCount; // miss的次数
/**
* @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, 并设置accessOrder为true.
}
/**
* Sets the size of the cache.
* 重新设置该cache的maxSize值
* @param maxSize The new maximum size.
*/
public void resize(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
synchronized (this) {
this.maxSize = maxSize;
}
trimToSize(maxSize); // 调用trimToSize()清除一些cache来使得当前缓存大小不大于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.
* 从cache中根据key获取数据
*/
public final V get(K key) {
if (key == null) { //key值不能为null.
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key); // 从map中获取值
if (mapValue != null) {
hitCount++; // 值不为null则命中加1
return mapValue; // 返回获取的值
}
missCount++; // mapValue为null, miss加1
}
/*
* 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.
*/
V createdValue = create(key); // 调用create()方法创建key对应的值,默认返回null, create方法可以重写.应该是为了创建一个默认值
if (createdValue == null) {
return null;
}
// 由于create方法在执行时没有进行同步, 所以不是线程安全的, 这会引发这样一个情况:当create方法在执行时, 有可能在别处又调用了put(key, value)方法, 使得该key有了对应的值
// 然后当你执行下面的代码就会产生了冲突, 具体表现就是mapValue的值不为null.下面就是用来处理这种情况的.并且加入了锁避免再次引发冲突.
synchronized (this) {
createCount++; // create加1
mapValue = map.put(key, createdValue); // 将key和创建的value存入map中
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue); // 如果产生了冲突, 即key对应了一个值,则使用原值覆盖create的值的方法来撤销第109行的put操作
} else {
size += safeSizeOf(key, createdValue); // 没有产生冲突, 即将key和createValue的大小加到当前大小size中.
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue); // 因为覆盖了createValue, 所以相当于remove了createValue, 调用entryRemoved方法
return mapValue;
} else {
trimToSize(maxSize);
// 调用trimToSize()清除一些cache来使得当前缓存大小不大于maxSize. 如果当前缓存大小本来就不大于maxSize就直接break不做操作.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 == null) { // key和value不能为null.
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++; // putCount的值加1
size += safeSizeOf(key, value); // 将要插入map中的数据的大小加到size中
previous = map.put(key, value); // 将数据插入到map中
if (previous != null) {
size -= safeSizeOf(key, previous); // 如果key已经对应了一个值则将原值对应的大小从当前的大小中减去
}
}
if (previous != null) {
entryRemoved(false, key, previous, value); // 覆盖了key在缓存中对应的原来的值, 相当于将原来的值remove了, 所以调用entryRemoved方法
}
trimToSize(maxSize);
// 调用trimToSize()清除一些cache来使得当前缓存大小不大于maxSize. 如果当前缓存大小本来就不大于maxSize就直接break不做操作.return previous;
}
/**
* Remove the eldest entries until the total of remaining entries is at or
* below the requested size.
* 清除cache(LinkedHashMap的accessOrder为true时,它里面存储的数据就是按照最近最少到最近最多的排序方式排好了)中的最近最少使用的数据remove, 直到当前的size不大于maxSize为止
* @param maxSize the maximum size of the cache before returning. May be -1
* to evict even 0-sized elements.
*/
public 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 || map.isEmpty()) { // 如果当前的size不大于maxSize或者map是空的就break, 不需要进行清除操作.
break;
}
// 上面if中的条件不满足, 需要进行清理工作.
Map.Entry<K, V> toEvict = map.entrySet().iterator().next(); //获取map中最近最少使用的数据,即链表中最开始的节点
key = toEvict.getKey(); // 获取要清除的数据的key
value = toEvict.getValue(); // 获取要清除的数据的value
map.remove(key); // remove操作
size -= safeSizeOf(key, value); // 更新size值
evictionCount++; // evictionCount大小加1.
}
entryRemoved(true, key, value, null); // 发生了remove操作, 调用entryRemoved方法
}
}
/**
* Removes the entry for {@code key} if it exists.
* 根据key从map中将对应的value 删除. 外界可以直接调用该方法进行删除操作.
* @return the previous value mapped by {@code key}.
*/
public final V remove(K key) {
if (key == null) { // key不能为null.
throw new NullPointerException("key == null");
}
V previous;
synchronized (this) {
previous = map.remove(key); // 核心操作, 从map中将key对应的值删除.
if (previous != null) {
size -= safeSizeOf(key, previous); // 更新size的值
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
// 发生了remove操作, 调用entryRemoved方法}
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.
* 用来根据key创造一个value, 在get方法中用于返回一个默认值:Null, 可以重写.
*/
protected V create(K key) {
return null;
}
private int safeSizeOf(K key, V value) { // 调用sizeOf方法获取value的大小并返回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.
* 获取value的大小, 该方法必须重写, 默认返回1.
* <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); // 创建了一个新的LinkedHashMap对象并将map中的值复制给它然后返回, 这样外界对返回的对象进行操作不会影响cache中的数据.
}
@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);
}
}
参考博客:
http://blog.csdn.net/chdjj/article/details/38701509?utm_source=tuicool&utm_medium=referral