您的位置:首页 > 移动开发 > Android开发

android LRUCache解析

2016-05-23 00:42 489 查看
LRU(Least Recently Used)最近最少使用算法

原理

缓存保存了一个强引用(Android 2.3开始,垃圾回收器更倾向于回收弱引用和软引用,软引用和弱引用变得不可靠,Android 3.0中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放)限制值的数量. 每当值被访问的时候,它会被移动到队列的头部. 当缓存已满的时候加入新的值时,队列中最后的值会出队,可能被回收

LRUCache内部维护主要是通过LinkedHashMap实现

这是一个安全的线程,多线程缓存通过同步实现


使用

默认情况下,缓存的大小是由值的数量决定,重写sizeOf计算不同的值

如果你缓存值需要明确释放,重写entryRemoved()

int maxMemory = (int) Runtime.getRuntime().maxMemory();
int mCacheSize = maxMemory / 8;
//给LruCache分配1/8 4M
mMemoryCache = new LruCache<String, Bitmap>(mCacheSize){

//必须重写此方法,来测量Bitmap的大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}

};


mMemoryCache.put(key, bitmap)


mMemoryCache.get(key)


这个类不允许有空的键值. get,put,remove 返回空值,key对应的值不在缓存中

源码分析

构造函数,初始化了最大容量和LinkedHashMap

/**
* @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,将accessOrder设置为true时,可以使遍历顺序和访问顺序一致,其内部双向链表将会按照近期最少访问到近期最多访问的顺序排列Entry对象

put方法,首先不允许键值为空,然后是线程安全,put的次数加一,size增加,以键值对的形式存入LinkedHashMap,如果之前已经存在了这个键值对,size减少成原来的大小,如果容量超过maxsize,将会删除最近很少访问的entry

/**
* 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) {
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;
}


put方法有一个很关键的地方超过最大值是会删除最近最少访问的

trimToSize首先线程安全,检查当前大小是否大于最大值,如果大于最大值,从LinkedHashMap中去除最近最少(循环删除链表首部元素)被访问的元素,获得键值,删除

/**
* 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.
*/
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) {
break;
}

Map.Entry<K, V> 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);
}
}


get方法,首先key不能为空,线程安全,根据key,从LinkedHashMap中获得value,不为空的话返回,为空的话,创建一个key,创建失败返回null,创建成功,在LinkedHashMap中创建键值对,存在就覆盖,不存在size增加,返回value值

/**
* 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.
*/
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++;
}

/*
* 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);
if (createdValue == null) {
return null;
}

synchronized (this) {
createCount++;
mapValue = 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;
}
}


核心代码分析完毕,想知道LinkedHashMap,请听下回哔哔

注:本文源码来自api 23
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息