LruCache
Picasso自己重写了Lrucache,构造方法如下
/** Create a cache using an appropriate portion of the available RAM as the maximum size. */
public LruCache(@NonNull Context context) {
this(Utils.calculateMemoryCacheSize(context));
}
static int calculateMemoryCacheSize(Context context) {
ActivityManager am = getService(context, ACTIVITY_SERVICE);
//判断应用是否开启了大内存
boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
//获取当前应用可用内存大小,单位为M
int memoryClass = am.getMemoryClass();
//当开启了大内存并且sdk>=3.0,重新获取当前应用可用内存大小
if (largeHeap && SDK_INT >= HONEYCOMB) {
memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);
}
//Target ~15% of the available heap.使用应用可用内存大小的15%作为LruCache缓存的大小
return (int) (1024L * 1024L * memoryClass / 7);
}
初始化一个LinkedHashMap,以后下载下来的图片的bitmap将存在这个map中
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("Max size must be positive.");
}
this.maxSize = maxSize;
//LinkedHashMap是一个哈希表+双向链表的结构,迭代顺序为先进先出
//构造方法有三个参数
//第一个参数表示map初始化容量的大小
//第二个表示加载因子,假如map的大小为10,当map中有第八个元素的时候(8>10*0.75),map的大小将扩充为20
//第三个元素表示是否按照访问顺序进行排序
//当第三个参数为true,那么map的迭代顺序则是按照最后进行访问的元素进行迭代,get、put操作都算元素的访问
this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
}
因为LinkedHashMap线程不安全,所以在多线程下面有关map的操作都需要加锁。执行map.get操作后,get到的元素将会被放在链表的尾部
@Override public Bitmap get(@NonNull String key) {
if (key == null) {
throw new NullPointerException("key == null");
}
Bitmap mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
return null;
}
@Override public void set(@NonNull String key, @NonNull Bitmap bitmap) {
if (key == null || bitmap == null) {
throw new NullPointerException("key == null || bitmap == null");
}
//得到bitmap占用的内存的大小
int addedSize = Utils.getBitmapBytes(bitmap);
//这个bitmap的大小如果大于缓存大小,则不保存
if (addedSize > maxSize) {
return;
}
synchronized (this) {
putCount++;
//现缓存大小加上将新添加的bitmap的大小
size += addedSize;
Bitmap previous = map.put(key, bitmap);
//put操作如果不返回空,新的bitmap替换了就的bitmap,所以要将缓存的大小减去就的bitmap的大小
if (previous != null) {
size -= Utils.getBitmapBytes(previous);
}
}
trimToSize(maxSize);
}
新加入了bitmap后,计算现缓存的大小有没有超过maxsize,循环判断大小,如果超过则将表头的元素移除,迭代遍历的时候,是从表头开始
private void trimToSize(int maxSize) {
while (true) {
String key;
Bitmap value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(
getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
//当maxsize=-1时,会一直循环直到map为空退出该方法
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
//map移除元素后,因为安卓3.0过后,bitmap在堆内存中,所以不需要调用recycle方法也能被GC回收
map.remove(key);
size -= Utils.getBitmapBytes(value);
evictionCount++;
}
}
}
计算bitmap大小
static int getBitmapBytes(Bitmap bitmap) {
int result;
//api>=19
if (SDK_INT >= KITKAT) {
result = bitmap.getAllocationByteCount();
} else if (SDK_INT >= HONEYCOMB_MR1) { //api>=12
result = BitmapHoneycombMR1.getByteCount(bitmap);
} else {
result = bitmap.getRowBytes() * bitmap.getHeight();
}
if (result < 0) {
throw new IllegalStateException("Negative size: " + bitmap);
}
return result;
}