Picasso中LRU缓存的设计分析

最近决定研究Picasso的源码,通过博客记录一些细节。

Cache

首先Picasso定义了一个Cache的接口,定义了对cache缓存的基本操作,如:
1、增删改查的方法
- get
- clear
- size
- set
- clearKeyUri

2、容量相关的属性
- maxSize
- size
接口的设计是为了方便不同缓存数据的结构实现。

/**
 * A memory cache for storing the most recently used images.
 * <p>
 * <em>Note:</em> The {@link Cache} is accessed by multiple threads. You must ensure
 * your {@link Cache} implementation is thread safe when {@link Cache#get(String)} or {@link
 * Cache#set(String, android.graphics.Bitmap)} is called.
 */
public interface Cache {
  /** Retrieve an image for the specified {@code key} or {@code null}. */
  Bitmap get(String key);

  /** Store an image in the cache for the specified {@code key}. */
  void set(String key, Bitmap bitmap);

  /** Returns the current size of the cache in bytes. */
  int size();

  /** Returns the maximum size in bytes that the cache can hold. */
  int maxSize();

  /** Clears the cache. */
  void clear();

  /** Remove items whose key is prefixed with {@code keyPrefix}. */
  void clearKeyUri(String keyPrefix);

  /** A cache which does not store any values. */
  Cache NONE = new Cache() {
    @Override public Bitmap get(String key) {
      return null;
    }

    @Override public void set(String key, Bitmap bitmap) {
      // Ignore.
    }

    @Override public int size() {
      return 0;
    }

    @Override public int maxSize() {
      return 0;
    }

    @Override public void clear() {
    }

    @Override public void clearKeyUri(String keyPrefix) {
    }
  };
}

LruCache

LruCache实现了Cache的接口,LruCache通过名称就可以看出使用的缓存策略是LRU策略,LruCache默认的大小设备上最大可申请内存的1/7,当然也可以通过外部来指定相关大小,使用键值对的方式通过set方法来添加或替换存储在LinkedHashMap中的缓存,通过lru算法,调用trimToSize(int maxSize)移除最少使用的bitmap。其实在LruCache中实现LRU算法的是LinkedHashMap。而LruCache更多的是管理缓存的操作。

/** A memory cache which uses a least-recently used eviction policy. */
public class LruCache implements Cache {
  final LinkedHashMap<String, Bitmap> map;
  private final int maxSize;

  private int size;
  private int putCount;
  private int evictionCount;
  private int hitCount;
  private int missCount;

  /** Create a cache using an appropriate portion of the available RAM as the maximum size. */
  public LruCache(Context context) {
    this(Utils.calculateMemoryCacheSize(context));
  }

  /** Create a cache with a given maximum size in bytes. */
  public LruCache(int maxSize) {
    if (maxSize <= 0) {
      throw new IllegalArgumentException("Max size must be positive.");
    }
    this.maxSize = maxSize;
    this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
  }

  @Override public Bitmap get(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(String key, Bitmap bitmap) {
    if (key == null || bitmap == null) {
      throw new NullPointerException("key == null || bitmap == null");
    }

    Bitmap previous;
    synchronized (this) {
      putCount++;
      size += Utils.getBitmapBytes(bitmap);
      previous = map.put(key, bitmap);
      //如果是替换则移除在size中移除previous的大小
      if (previous != null) {
        size -= Utils.getBitmapBytes(previous);
      }
    }

    trimToSize(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!");
        }

        if (size <= maxSize || map.isEmpty()) {
          break;
        }
        //超出最大缓存值,则使用lru算法移除最少使用的bitmap
        Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
        key = toEvict.getKey();
        value = toEvict.getValue();
        map.remove(key);
        size -= Utils.getBitmapBytes(value);
        evictionCount++;
      }
    }
  }

  /** Clear the cache. */
  public final void evictAll() {
    trimToSize(-1); // -1 will evict 0-sized elements
  }

  @Override public final synchronized int size() {
    return size;
  }

  @Override public final synchronized int maxSize() {
    return maxSize;
  }

  @Override public final synchronized void clear() {
    evictAll();
  }
  //通过key来移除指定的缓存entry
  @Override public final synchronized void clearKeyUri(String uri) {
    boolean sizeChanged = false;
    int uriLength = uri.length();
    for (Iterator<Map.Entry<String, Bitmap>> i = map.entrySet().iterator(); i.hasNext();) {
      Map.Entry<String, Bitmap> entry = i.next();
      String key = entry.getKey();
      Bitmap value = entry.getValue();
      int newlineIndex = key.indexOf(KEY_SEPARATOR);
      if (newlineIndex == uriLength && key.substring(0, newlineIndex).equals(uri)) {
        i.remove();
        size -= Utils.getBitmapBytes(value);
        sizeChanged = true;
      }
    }
    if (sizeChanged) {
      trimToSize(maxSize);
    }
  }

  /** Returns the number of times {@link #get} returned a value. */
  public final synchronized int hitCount() {
    return hitCount;
  }

  /** Returns the number of times {@link #get} returned {@code null}. */
  public final synchronized int missCount() {
    return missCount;
  }

  /** Returns the number of times {@link #set(String, Bitmap)} was called. */
  public final synchronized int putCount() {
    return putCount;
  }

  /** Returns the number of values that have been evicted. */
  public final synchronized int evictionCount() {
    return evictionCount;
  }
}

LinkedHashMap

LinkedHashMap通过名称可以看出是LinkedList+HashMap的结构,下面是LinkedHashMap构造方法及参数说明

  /**
     * Constructs a new {@code LinkedHashMap} instance with the specified
     * capacity, load factor and a flag specifying the ordering behavior.
     *
     * @param initialCapacity
     *            the initial capacity of this hash map.
     * @param loadFactor
     *            the initial load factor.
     * @param accessOrder
     *            {@code true} if the ordering should be done based on the last
     *            access (from least-recently accessed to most-recently
     *            accessed), and {@code false} if the ordering should be the
     *            order in which the entries were inserted.
     *            如果accessOrder为true,通过lru,否则通过插入的顺序排序
     * @throws IllegalArgumentException
     *             when the capacity is less than zero or the load factor is
     *             less or equal to zero.
     */
    public LinkedHashMap(
            int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor);
        init();
        this.accessOrder = accessOrder;
    }

从上面的构造方法说明可以看出accessOrder参数,是决定是否使用LRU算法的标志。

即:指定accessOrder为true,则LinkedHashMap数据使用LRU算法进行排序,通过迭代器循环遍历时最近最少使用的元素会在迭代器访问的最开始的位置。而accessOrder为false的元素LinkedHashMap会根据插入元素的顺序访问,可以说是一个FIFO的队列结构。


关于LinkedHashMap的源码分析和举例说明,强烈推荐这篇博客 http://www.cnblogs.com/xrq730/p/5052323.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值