介绍

LruCache 是Android3.1提供的一个缓存类,用于数据缓存,一般用于图片的内存缓存。Lru的英文是Least Recently Used,也就是近期最少使用算法,核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。

当我们进行网络加载图片的时候,肯定要对图片进行缓存,这样下次加载图片就可以直接从缓存中取。三级缓存大家应该都比较熟悉,内存,硬盘和网络。所以一般要进行内存缓存和硬盘缓存,其中内存缓存就是用的LruCache。

使用

public class MyImageLoader {
    private LruCache mLruCache;public MyImageLoader() {int maxMemory = (int) (Runtime.getRuntime().maxMemory())/1024;int cacheSize = maxMemory / 8;
        mLruCache = new LruCache(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes()*value.getHeight()/1024;
            }
        };
    }/**
     * 添加图片缓存
     */public void addBitmap(String key, Bitmap bitmap) {
            mLruCache.put(key, bitmap);
    }/**
     * 从缓存中获取图片
     *
     */public Bitmap getBitmap(String key) {return mLruCache.get(key);
    }
}
public class MyImageLoader {
    private LruCache mLruCache;public MyImageLoader() {int maxMemory = (int) (Runtime.getRuntime().maxMemory())/1024;int cacheSize = maxMemory / 8;
        mLruCache = new LruCache(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes()*value.getHeight()/1024;
            }
        };
    }/**
     * 添加图片缓存
     */public void addBitmap(String key, Bitmap bitmap) {
            mLruCache.put(key, bitmap);
    }/**
     * 从缓存中获取图片
     *
     */public Bitmap getBitmap(String key) {return mLruCache.get(key);
    }
}

使用方法如上,只需要提供缓存的总容量大小并重写sizeOf方法计算缓存对象大小即可。这里总容量的大小也是通用方法,即进程可用内存的1/8,单位kb。然后就可以使用put方法来添加缓存对象,get方法来获取缓存对象。

原理

原理其实也很简单,就是用到了LRU算法,内部使用LinkedHashMap 进行存储。在缓存满了之后,会将最近最少使用的元素移除。怎么保证找到这个最近最少的元素呢?就是每次使用get方法访问了元素或者增加了一个元素,就把元素移动到LinkedHashMap的尾部,这样第一个元素就是最不经常使用的元素,在容量满了之后就可以将它移除。

简单看看源码:

public LruCache(int maxSize) {
       if (maxSize <= 0) {
           throw new IllegalArgumentException("maxSize <= 0");
       }
       this.maxSize = maxSize;
       this.map = new LinkedHashMap(0, 0.75f, true);
   }public final V put(K key, V value) {if (key == null || value == null) {throw new NullPointerException("key == null || value == null");
       }
       V previous; //查找是否已经存在key对应的元素synchronized (this) {
           putCount++;//计算entry的大小
           size += safeSizeOf(key, value); 
           previous = map.put(key, value);if (previous != null) {//如果之前存在,这先减去之前那个entry所占用的内存大小
               size -= safeSizeOf(key, previous);
           }
       }if (previous != null) {//如果之前存在则调用entryRemoved回调子类重写的此方法,做一些处理
           entryRemoved(false, key, previous, value);
       }//根据最大的容量,计算是否需要淘汰掉最不常使用的entry
       trimToSize(maxSize);return previous;
   }public final V get(K key) {if (key == null) {throw new NullPointerException("key == null");
      }
      V mapValue;//根据key来查询符合条件的etnrysynchronized (this) {
          mapValue = map.get(key);if (mapValue != null) {
              hitCount++;return mapValue;
          }
          missCount++;
      }/*
       * Attempt to create a value. This may take a long time, and the map
       * may be different when create() returns. If a conflicting value was
       * added to the map while create() was working, we leave that value in
       * the map and release the created value.
       */
      V createdValue = create(key);if (createdValue == null) {return null;
      }synchronized (this) {
          createCount++;//mapValue返回的是已经存在相同key的entry
          mapValue = map.put(key, createdValue);if (mapValue != null) {// There was a conflict so undo that last put
              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 LruCache(int maxSize) {
       if (maxSize <= 0) {
           throw new IllegalArgumentException("maxSize <= 0");
       }
       this.maxSize = maxSize;
       this.map = new LinkedHashMap(0, 0.75f, true);
   }public final V put(K key, V value) {if (key == null || value == null) {throw new NullPointerException("key == null || value == null");
       }
       V previous; //查找是否已经存在key对应的元素synchronized (this) {
           putCount++;//计算entry的大小
           size += safeSizeOf(key, value); 
           previous = map.put(key, value);if (previous != null) {//如果之前存在,这先减去之前那个entry所占用的内存大小
               size -= safeSizeOf(key, previous);
           }
       }if (previous != null) {//如果之前存在则调用entryRemoved回调子类重写的此方法,做一些处理
           entryRemoved(false, key, previous, value);
       }//根据最大的容量,计算是否需要淘汰掉最不常使用的entry
       trimToSize(maxSize);return previous;
   }public final V get(K key) {if (key == null) {throw new NullPointerException("key == null");
      }
      V mapValue;//根据key来查询符合条件的etnrysynchronized (this) {
          mapValue = map.get(key);if (mapValue != null) {
              hitCount++;return mapValue;
          }
          missCount++;
      }/*
       * Attempt to create a value. This may take a long time, and the map
       * may be different when create() returns. If a conflicting value was
       * added to the map while create() was working, we leave that value in
       * the map and release the created value.
       */
      V createdValue = create(key);if (createdValue == null) {return null;
      }synchronized (this) {
          createCount++;//mapValue返回的是已经存在相同key的entry
          mapValue = map.put(key, createdValue);if (mapValue != null) {// There was a conflict so undo that last put
              map.put(key, mapValue);
          } else {
              size += safeSizeOf(key, createdValue);
          }
      }if (mapValue != null) {
          entryRemoved(false, key, createdValue, mapValue);return mapValue;
      } else {
          trimToSize(maxSize);return createdValue;
      }
  }

其实可以看到,LruCache类本身做的事情不多,限定了缓存map的大小,然后利用了LinkHashMap完成了LRU的缓存策略。所以主要的实现LRU逻辑部分还是在LinkHashMap中。LinkedHashMaphashmap链表的结合体,通过链表来记录元素的顺序和链接关系,通过HashMap来存储数据,它可以控制元素的被遍历时候输出的顺序。他是一个双向链表,上面说过他会把最近访问的元素放到队列的尾部,有兴趣的可以看看LinkHashMap的源码。