LruCache源码分析

使用强引用来缓存有限数量的值.每次被访问的值,将会移动到队列的头部。当队列大小超过缓存大小时,队列尾部的值将会被删除并且允许垃圾回收器将它回收。

如果你需要明确知道缓存数据什么时候被释放,需要重写 entryRemoved();方法

如果出现缓存缺失时,可以重写create();方法。使用这种简单的方法,可以确保永远存在返回值。

默认的,缓存大小是根据缓存项数量来计算的。可以重写sizeOf()方法来设置不同的计算缓存大小的单位。例如用于缓存4MiB的图片:

int cacheSize = 4 * 1024 * 1024;//用于计算缓存总Size
LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize){
  protected int sizeOf(String key, Bitmap value){
    return value.getByteCount();//返回每一张图片Size
  }
}

这个类是线程安全的,多个缓存的原子操作代码:

syschronized(cache){
  if(cache.get(key) == null){
    cache.put(key, value);
  }
}

这个类不允许 null 作为key或者value。如果get(),put()remove()方法中返回值为null,那么就明确表明,缓存中不存在这个值。

这个类在Anroid3.1加入,如果需要在之前版本中使用,请使用support包中程序。

成员变量

private final LinkedHashMap<K, V> map;//Cache 缓存的主要是实现基础。

private int size;//基于某个单位上的缓存大小,不一定等于缓存成员数量。具体数量和size的转换,通过方法sizeOf()实现。
private int maxSize;//初始化时传入的,缓存最大值。

private int putCount;//put()方法调用次数。
private int createCount;//create()方法调用次数。
private int evictionCount;//超出限制,被删除的数量。
private int hitCount;//返回get()方法正常获取缓存数据次数。
private int missCount;//返回get()方法为在缓存中获取数据的次数。

构造方法

public LruCache(int maxSize) {
      this.maxSize = maxSize;
      this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
  }

  在构造函数中,初始化了成员变量中的LinkedHashMap()。LRUCache主要实现感觉更多的是LinkedHashMap的学习。
LinkedHashMap是HashMap增加一个双向链表。 所以LinkedHashMap可以插入顺序或者访问顺序访问。
LinkedHashMap构造参数中最后一个参数accessOrder,用来判断是使用哪种顺序。accressOrder等于true为访问顺序,
等于false 为数据插入顺序。

  LinkedHashMap重写了HashMap的Entry。增加了before和after,使Entry虽然存在HashMap中但是另外也存在一个双向链表。因此LinkedHashMap可以方便的通过访问和插入顺序来访问某个Entry。也正是因为LinkedHashMap存在这个访问数据顺序序,所以用它来做LRUCache,方便删除最早之前加入的数据,并且可以根据访问顺序来调整链表顺序。

关于LinkedHashMap推荐博客:http://blog.csdn.net/justloveyou_/article/details/71713781

get()方法

public final V get(K key) {
       V mapValue;
       synchronized (this) {                 
         //从map中查找数据
           mapValue = map.get(key);   
           if (mapValue != null) {
             //存在则直接返回。并且查找成功计数变量hitCount++
               hitCount++;
               return mapValue;
           }
           //查找失败计数变量missCount++
           missCount++;                    
       }
       /*
        * 下面尝试使用 create()方法创建一个新值,加到到map中。而且如果新值存在冲突,将会保留以前的值。
        * create()方法默认返回值为null。
        */
       V createdValue = create(key);
       if (createdValue == null) {
           return null;
       }
       synchronized (this) {
           createCount++;
           //将创建的值添加到map中,并返回key以前对应的value。
           mapValue = map.put(key, createdValue);    
           //key以前存在对应值,则将以前的值重新添加到map中,舍弃新创建值。
           //否则将size增加新建value 所占size。
           if (mapValue != null) {
               map.put(key, mapValue);
           } else {
               size += safeSizeOf(key, createdValue);
           }
       }

      //mapValue不为null,使用mapValue替换了createValue,调用entryRemoved()。
      //为null,则重新计算是否越界。
       if (mapValue != null) {
           entryRemoved(false, key, createdValue, mapValue);
           return mapValue;
       } else {
           trimToSize(maxSize);
           return createdValue;
       }
   }

put方法

  /**
   * 将键值对插入到队列头部。
   * @return 返回以前与这个key相对应的Value。
   */
  public final V put(K key, V value) {

      V previous;
      synchronized (this) {
          putCount++;
          size += safeSizeOf(key, value);       //当前总size加上新加入Entry的size
          previous = map.put(key, value);       //加入Map中
           //如果以前与key相对应value不为空,那么size减去那个value占据size。
          if (previous != null) {             
              size -= safeSizeOf(key, previous);
          }
      }

      //因为删除了previous  所以调用entryRemoved()方法。
      if (previous != null) {
          entryRemoved(false, key, previous, value);
      }

      trimToSize(maxSize);
      return previous;
  }

remove()方法

   /**
    * 移除被 key对应的entry,如果不存在返回null。
    */
   public final V remove(K key) {

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

resize()方法和trimToSize()方法和evictAll()

  /**
   * 设置缓存size
   */
  public void resize(int maxSize) {
      if (maxSize <= 0) {
          throw new IllegalArgumentException("maxSize <= 0");
      }

      synchronized (this) {
          this.maxSize = maxSize;
      }
      trimToSize(maxSize);
  }

  /**
   * 删除超出MaxSize的最早加入的entry。
   */
  public void trimToSize(int maxSize) {
      while (true) {
          K key;
          V value;
          synchronized (this) {

              if (size <= maxSize) {   
                  break;
              }

              //返回map中最古老的entry,如果map为空则返回null。          
              Map.Entry<K, V> toEvict = map.eldest();
              if (toEvict == null) {
                  break;
              }

              key = toEvict.getKey();
              value = toEvict.getValue();
              //从map中移除最古老的entry
              map.remove(key);       
              //size进行相应的减少
              size -= safeSizeOf(key, value);
              //修改被移除次数
              evictionCount++;
          }
          //因为最古老的entry的呗删除了,所以调用entryRemoved().
          entryRemoved(true, key, value, null);
      }
  }

  /**
   * 清空缓存
   */
  public final void evictAll() {
      trimToSize(-1); // -1 will evict 0-sized elements
  }

部分protected 方法


    /**
    * 返回entry中每个value对应的在总size中占据的大小。上面说明中通过重写该方法,
    * 实现一个用于缓存Bitmap的LruCache
    */
    protected int sizeOf(K key, V value) {
      return 1;
    }
    /**
    *当entry被remove或者被put方法替换或者被删除最古老值时 调用此方法
    *evicted 为true为删除掉最古老值时调用 ;false为remove()或者put()方法中调用。
    */
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

    /**
     *当调用get方法,没有与key相对的值时调动。
     */
    protected V create(K key) {
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值