标签:android 强应用 内存溢出 缓存 lrucache
/***************************************************
* TODO: description .
* @author: gao_chun
* @since: 2015-4-7
* @version: 1.0.0
* @remark: 转载请注明出处
**************************************************/
内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache (此类在android-support-v4的包中提供) 。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在
LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
在过去,我们经常会使用一种非常流行的内存缓存技术的实现,即软引用或弱引用 (SoftReference or WeakReference)。但是现在已经不再推荐使用这种方式了,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃。
为了能够选择一个合适的缓存大小给LruCache, 有以下多个因素应该放入考虑范围内,例如:
你的设备可以为每个应用程序分配多大的内存?
设备屏幕上一次最多能显示多少张图片?
有多少图片需要进行预加载,因为有可能很快也会显示在屏幕上?
你的设备的屏幕大小和分辨率分别是多少?
一个超高分辨率的设备(例如 Galaxy Nexus) 比起一个较低分辨率的设备(例如 Nexus S),在持有相同数量图片的时候,需要更大的缓存空间。
图片的尺寸和大小,还有每张图片会占据多少内存空间。
图片被访问的频率有多高?会不会有一些图片的访问频率比其它图片要高?
如果有的话,你也许应该让一些图片常驻在内存当中,或者使用多个LruCache 对象来区分不同组的图片。
你能维持好数量和质量之间的平衡吗?有些时候,存储多个低像素的图片,而在后台去开线程加载高像素的图片会更加的有效。
并没有一个指定的缓存大小可以满足所有的应用程序,这是由你决定的。你应该去分析程序内存的使用情况,然后制定出一个合适的解决方案。一个太小的缓存空间,有可能造成图片频繁地被释放和重新加载,这并没有好处。而一个太大的缓存空间,则有可能还是会引起 java.lang.OutOfMemory 的异常。
搜索并整理了一些资料:
-
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的大小。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
* 不允许key或者value为null 当get(),put(),remove()返回值为null时,key相应的项不在cache中
-
-
public class LruCache<K, V> {
-
private final LinkedHashMap<K, V> map;
-
-
-
private int size;
//已经存储的大小
-
private int maxSize;
//规定的最大存储空间
-
-
private int putCount;
//put的次数
-
private int createCount;
//create的次数
-
private int evictionCount;
//回收的次数
-
private int hitCount;
//命中的次数
-
private int missCount;
//丢失的次数
-
-
-
-
-
-
-
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);
-
}
-
-
-
-
-
-
通过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);
-
if (mapValue != null) {
-
hitCount++; //命中
-
return mapValue;
-
}
-
missCount++; //丢失
-
}
-
-
-
-
-
-
-
-
-
-
V createdValue = create(key);
-
if (createdValue == null) {
-
return null;
-
}
-
-
synchronized (this) {
-
createCount++;//创建++
-
mapValue = map.put(key, createdValue);
-
-
if (mapValue != null) {
-
-
//
-
map.put(key, mapValue);
-
} else {
-
size += safeSizeOf(key, createdValue);
-
}
-
}
-
-
if (mapValue != null) {
-
entryRemoved(false, key, createdValue, mapValue);
-
return mapValue;
-
} else {
-
trimToSize(maxSize);
-
return createdValue;
-
}
-
}
-
-
-
-
-
-
-
-
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) {
//返回的先前的value值
-
size -= safeSizeOf(key, previous);
-
}
-
}
-
-
if (previous != null) {
-
entryRemoved(false, key, previous, value);
-
}
-
-
trimToSize(maxSize);
-
return previous;
-
}
-
-
-
-
-
* 清空cache空间
-
-
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<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);
-
}
-
}
-
-
-
-
删除key相应的cache项,返回相应的value
-
-
-
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;
-
}
-
-
-
-
-
-
-
当item被回收或者删掉时调用。改方法当value被回收释放存储空间时被remove调用,
-
* 或者替换item值时put调用,默认实现什么都没做。
-
-
-
-
-
-
* true---为释放空间被删除;false---put或remove导致
-
-
-
-
-
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
-
-
-
-
-
-
当某Item丢失时会调用到,返回计算的相应的value或者null
-
-
-
-
-
-
-
-
-
-
-
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;
-
}
-
-
-
-
-
-
返回用户定义的item的大小,默认返回1代表item的数量,最大size就是最大item值
-
-
-
protected int sizeOf(K key, V value) {
-
return 1;
-
}
-
-
-
-
* 清空cacke
-
-
public final void evictAll() {
-
trimToSize(-1);
-
}
-
-
-
-
-
-
-
public synchronized final int size() {
-
return size;
-
}
-
-
-
-
-
-
-
public synchronized final int maxSize() {
-
return maxSize;
-
}
-
-
-
-
-
-
public synchronized final int hitCount() {
-
return hitCount;
-
}
-
-
-
-
-
-
public synchronized final int missCount() {
-
return missCount;
-
}
-
-
-
-
-
public synchronized final int createCount() {
-
return createCount;
-
}
-
-
-
-
-
public synchronized final int putCount() {
-
return putCount;
-
}
-
-
-
-
* 返回被回收的数量
-
-
public synchronized final int evictionCount() {
-
return evictionCount;
-
}
-
-
-
-
返回当前cache的副本,从最近最少访问到最多访问
-
-
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);
-
}
-
}
简析Android中LruCache缓存类
标签:android 强应用 内存溢出 缓存 lrucache
原文地址:http://blog.csdn.net/gao_chun/article/details/44922019