lruCache使用用于图片缓存
2015-03-04 15:42
197 查看
在开发android app时当应用加载大量图片时,我们必须要使用缓存技术来处理内存溢出问题,缓存技术主要可以分为内部缓存和外部缓存,这一节主要讲一下内部缓存一种实现。
LruCache缓存技术在android3.1(api12)添加的,位于android.util.lruCache,下面首先看一下文档介绍:
[java] view
plaincopy
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.
这里主要大体内容是:该缓存持有的是强引用,每当对象值被访问时,这个值对象会被移动到队列的头部。当添加缓存对象到缓存,缓存队列已满时,在缓存队列尾部的对象会被踢出,并被垃圾回收器回收。
从上面文档介绍来看LruCache是使用队列来管理缓存对象,使用频率较高的对象会被放到队列的头部,不长使用的对象位于队列尾部,当缓存达到最大值时,优先回收队列尾部不经常使用的缓存对象。
下面进入源码详细分析
[java] view
plaincopy
/**
* @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实现传入true表示队列是有序的,
再看put()方法实现:
[java] view
plaincopy
/**
* 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;
}
从方法开始我们可以看出对于加入缓存的key,value均不能为空,否则会有异常抛出,所以我们put时需要进行必要判断处理。LruCache对每个放入缓存对象大小进行计算,加入总缓存大小。这里需要注意的是如果新加入的key值原来已经存在,即原缓存被覆盖时,需要将原对象缓存大小从总缓存中去掉,最后对整个缓存大小进行判断是否超过最大缓存数值并进行处理。
下面看一下缓存判断逻辑处理方法trimToSize
[java] view
plaincopy
/**
* @param maxSize
* the maximum size of the cache before returning. May be -1 to
* evict even 0-sized elements.
*/
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 || map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
这里逻辑比较简单:这里采用循环判断,如果缓存queue当前大小超过最大值就删除队列中末尾不常用缓存对象,直到缓存达到在最大限制值之内。
在上述两个方法内我们会看的entryRemoved方法会被调用,源码如下:
[java] view
plaincopy
/**
* 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) {
}
居然是空,看来是为我们提供的接口。当缓存被覆盖或者被移除缓存队列时会调用entryRemoved()方法,第一个参数evicted true表示已达到缓存最大值被剔除,false表示缓存被新对象覆盖,我们可以重新该方法来监听哪个缓存对象值被回收了。值得注意的地方是当该方法被执行时,缓存对象被剔除并不是该对象立即被回收掉,可能还需要一定时间才能被垃圾回收器回收掉,如果内存资源十分紧张,我们就需要适当加速帮助垃圾回收器及时回收,这里已bitmap为例,需要调用oldValue
recycle 来加速垃圾回收。
最后看get方法实现:
[java] view
plaincopy
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);
}
}
get实现也很简单根据传入的key值进行取值,如果缓存对象存在直接返回,对象不存在则调用create方法。其中hitCount++为计算成功获取到值对象次数,missCount获取失败次数。顺便说一下create(),create()默认返回为null,我们可以重写此方法来处理获取缓存失败时处理逻辑,通常是调用put,网上很多例子都是另外自己写方法处理,其实完全没有那必要。直接重新即可,不用自己进行判断处理了,Lruchache已经提供好了接口,何必另造轮子?
通过以上简单分析已经对Lrucache原理大致了解差不多了,对于使用我们就更加轻松了。
LruCache缓存技术在android3.1(api12)添加的,位于android.util.lruCache,下面首先看一下文档介绍:
[java] view
plaincopy
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.
这里主要大体内容是:该缓存持有的是强引用,每当对象值被访问时,这个值对象会被移动到队列的头部。当添加缓存对象到缓存,缓存队列已满时,在缓存队列尾部的对象会被踢出,并被垃圾回收器回收。
从上面文档介绍来看LruCache是使用队列来管理缓存对象,使用频率较高的对象会被放到队列的头部,不长使用的对象位于队列尾部,当缓存达到最大值时,优先回收队列尾部不经常使用的缓存对象。
下面进入源码详细分析
[java] view
plaincopy
/**
* @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实现传入true表示队列是有序的,
再看put()方法实现:
[java] view
plaincopy
/**
* 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;
}
从方法开始我们可以看出对于加入缓存的key,value均不能为空,否则会有异常抛出,所以我们put时需要进行必要判断处理。LruCache对每个放入缓存对象大小进行计算,加入总缓存大小。这里需要注意的是如果新加入的key值原来已经存在,即原缓存被覆盖时,需要将原对象缓存大小从总缓存中去掉,最后对整个缓存大小进行判断是否超过最大缓存数值并进行处理。
下面看一下缓存判断逻辑处理方法trimToSize
[java] view
plaincopy
/**
* @param maxSize
* the maximum size of the cache before returning. May be -1 to
* evict even 0-sized elements.
*/
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 || map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
这里逻辑比较简单:这里采用循环判断,如果缓存queue当前大小超过最大值就删除队列中末尾不常用缓存对象,直到缓存达到在最大限制值之内。
在上述两个方法内我们会看的entryRemoved方法会被调用,源码如下:
[java] view
plaincopy
/**
* 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) {
}
居然是空,看来是为我们提供的接口。当缓存被覆盖或者被移除缓存队列时会调用entryRemoved()方法,第一个参数evicted true表示已达到缓存最大值被剔除,false表示缓存被新对象覆盖,我们可以重新该方法来监听哪个缓存对象值被回收了。值得注意的地方是当该方法被执行时,缓存对象被剔除并不是该对象立即被回收掉,可能还需要一定时间才能被垃圾回收器回收掉,如果内存资源十分紧张,我们就需要适当加速帮助垃圾回收器及时回收,这里已bitmap为例,需要调用oldValue
recycle 来加速垃圾回收。
最后看get方法实现:
[java] view
plaincopy
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);
}
}
get实现也很简单根据传入的key值进行取值,如果缓存对象存在直接返回,对象不存在则调用create方法。其中hitCount++为计算成功获取到值对象次数,missCount获取失败次数。顺便说一下create(),create()默认返回为null,我们可以重写此方法来处理获取缓存失败时处理逻辑,通常是调用put,网上很多例子都是另外自己写方法处理,其实完全没有那必要。直接重新即可,不用自己进行判断处理了,Lruchache已经提供好了接口,何必另造轮子?
通过以上简单分析已经对Lrucache原理大致了解差不多了,对于使用我们就更加轻松了。
相关文章推荐
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 应用开发 之使用LruCache和DiskLruCache来在内存和SD卡中缓存图片
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- [转载]Android使用 LruCache 缓存图片
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- [置顶] 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android使用 LruCache 缓存图片
- Android使用 LruCache 缓存图片
- Android使用 LruCache 缓存图片
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android 应用开发 之使用LruCache和DiskLruCache来在内存和SD卡中缓存图片
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android使用 LruCache 缓存图片
- 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- Android使用 LruCache 缓存图片
- Android使用 LruCache 缓存图片
- Android使用 LruCache 缓存图片
- Android使用 LruCache 缓存图片