Picasso 源码 学习(二) LruCache

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(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 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;
  }

你可能感兴趣的:(Android)