Android简单内存缓存

Earle96S 6年前
   <h3><strong>一. 问题</strong></h3>    <p>前段时间在公司项目中遇到一个问题,在集成百度地图时想要在地图上动态添加 ICON,这些 ICON 都是随时可变的,起初设计时,每次添加的 ICON 都是动态 new 出一个新的Bitmap,这样可以满足 ICON 随时可变的需求,但是需求完成之后,通过 AS 观察到 APP 内存飙升,APP 内存吃紧带来的坏处我就不再赘述了,为了解决这个问题,我就设计了一个简单的内存缓存框架来解决这个问题,有效的减少了 APP 的内存消耗。</p>    <h3><strong>二. 解决</strong></h3>    <p>解决问题时的问题思路我就不再说了,直接看下源码~</p>    <pre>  <code class="language-java">/**   * Created by daiyiming on 2016/12/10.   * 本地内存缓存   */  public final class MemoryCache<KO, VO> {        public static final int CAPACITY_DEFAULT = 10; // 默认缓存      public static final int CAPACITY_INFINITY = -1; // 无限缓存      public static final int MAX_CAPACITY_DEFAULT = 20; // 默认最大缓存      public static final int MAX_CAPACITY_INFINITY = -1; // 无限最大缓存        private final LinkedList<ValueHolder<KO, VO>> mCacheList; // 缓存      private final HashSet<KO> mKeySet; // 键缓存      private volatile int mCapacity; // 容量      private volatile int mMaxCapacity; // 最大容量      private volatile boolean mAllowUpdate; // 键值冲突时是否准许更新        /**       * 键值对组合类       *       * @param <KI> 键类型       * @param <VI> 值类型       */      private static final class ValueHolder<KI, VI> {          private final KI mKey; // 键不准许修改,若修改则用添加新的对象          private VI mValue; // 值可以修改,用于更新            private ValueHolder(KI key, VI value) {              mKey = key;              mValue = value;          }            /**           * 更新值对象           * @param value 新的值对象           */          private void update(VI value) {              recycleValue();              mValue = value;          }            /**           * 回收Holder           */          private void recycle() {              recycleKey();              recycleValue();          }            private void recycleKey() {              if (mKey instanceof IRecycleInterface) {                  ((IRecycleInterface) mKey).recycle();              }          }            private void recycleValue() {              if (mValue instanceof IRecycleInterface) {                  ((IRecycleInterface) mValue).recycle();              }          }        }        /**       * 值接口,实现帮助对象键值内存回收       */      public interface IRecycleInterface {          void recycle();      }        public MemoryCache() {          this(CAPACITY_DEFAULT, MAX_CAPACITY_DEFAULT);      }        public MemoryCache(int capacity, int maxCapacity) {          if (capacity < CAPACITY_INFINITY                  || maxCapacity < MAX_CAPACITY_INFINITY) {              throw new IllegalArgumentException("MemoryCache:构造函数参数错误");          }          mCacheList = new LinkedList<>();          mKeySet = new HashSet<>();          mCapacity = capacity;          mMaxCapacity = maxCapacity;          mAllowUpdate = false;      }        public synchronized void put(KO key, VO value) {          if (key == null || value == null) {              return;          }          if (mKeySet.contains(key)) { // 如果已经存在则复用对象              ListIterator<ValueHolder<KO, VO>> iterator = mCacheList.listIterator();              while (iterator.hasNext()) {                  ValueHolder<KO, VO> holder = iterator.next();                  if (holder.mKey.equals(key)) {                      holder.update(value);                      // 如果不准许更新则删除重新添加                      if (!mAllowUpdate && iterator.previousIndex() != 0) {                          iterator.remove();                          mCacheList.addFirst(holder);                      }                      break;                  }              }          } else { // 不存在则添加              mKeySet.add(key);              mCacheList.addFirst(new ValueHolder<>(key, value));          }          // 如果大于最大容量且不是无限容量则清除末尾一个          if (mMaxCapacity != MAX_CAPACITY_INFINITY                  && mCacheList.size() > mMaxCapacity) {              remove(mCacheList.size() - 1);          }      }        public synchronized VO get(KO key) {          if (mKeySet.contains(key)) {              ListIterator<ValueHolder<KO, VO>> iterator = mCacheList.listIterator();              while (iterator.hasNext()) {                  ValueHolder<KO, VO> holder = iterator.next();                  if (holder.mKey.equals(key)) { // 找到                      if (iterator.previousIndex() != 0) { // 如果不是在头部就移动到头部                          iterator.remove();                          mCacheList.addFirst(holder);                      }                      // 删除一个超出容量的数据                      if (mCapacity != CAPACITY_INFINITY                              && mCacheList.size() > mCapacity) {                          remove(mCacheList.size() - 1);                      }                      return holder.mValue;                  }              }          }          return null;      }        public synchronized boolean contains(KO k) {          return mKeySet.contains(k);      }        public synchronized void clear() {          mKeySet.clear();          for (ValueHolder<KO, VO> holder : mCacheList) {              holder.recycle();          }          mCacheList.clear();      }        public synchronized int size() {          return mCacheList.size();      }        public synchronized void remove(KO key) {          if (mKeySet.contains(key)) {              ListIterator<ValueHolder<KO, VO>> iterator = mCacheList.listIterator();              while (iterator.hasNext()) {                  ValueHolder<KO, VO> holder = iterator.next();                  if (holder.mKey.equals(key)) {                      iterator.remove();                      mKeySet.remove(holder.mKey);                      holder.recycle();                      break;                  }              }          }      }        public synchronized void remove(int position) {          if (position >= 0 && position < size()) {              ValueHolder<KO, VO> removedHolder = mCacheList.remove(position);              mKeySet.remove(removedHolder.mKey);              removedHolder.recycle();          }      }        public void setCapacity(int capacity) {          mCapacity = capacity;      }        public void setMaxCapacity(int maxCapacity) {          mMaxCapacity = maxCapacity;      }        public int getCapacity() {          return mCapacity;      }        public int getMaxCapacity() {          return mMaxCapacity;      }        /**       * 准许更新       * 决定添加过程中键值重复时直接原位置更新还是删除重新添加       *       * @param allowUpdate 是否准许更新       */      public void allowUpdate(boolean allowUpdate) {          mAllowUpdate = allowUpdate;      }    }</code></pre>    <p>通过一个链表保存需要缓存的元素,每次添加的新元素放到链表的首部,添加时检测如果超出最大容量,则从链表的尾部删除一个元素,为什么这么做呢?主要是为了防止疯狂 put 元素导致 OOM。在每次get元素的时候,通过传入的 key 值遍历链表,当命中想要获取的元素的时候,将这个元素移到链表的首部,这样可以保证频繁获取的元素在链表靠前的位置,减少获取时间。在 get 成功的时候,如果链表长度大于容量(注意与最大容量的区别),我们就从列表末尾删除一个元素,因为末尾的元素表明这个元素很大的可能是长时间没人使用(get操作),即可视为过期元素。</p>    <p>为什么要设置一个容量和最大容量的区别呢?因为考虑到可能存在疯狂 put 的操作,即用户一直在 put,如果最大容量大于容量,就给了最先 put 的元素有被重新拿到链表首部的可能性,即一定程度的加强了这个元素被再次利用的可能性,在之后的 get 的操作中,链表尾部元素逐渐被清除,链表长度逐渐回归正常。又因为设置了最大容量,给 put 操作设置了上限,所以基本不会有 OOM。</p>    <p>当然防止有些时候需要缓存的时候 IO 连接,SOCKET 连接,Bitmap 等需要手动销毁的东西,我还实现了一个 Recycle 接口,如果缓存的键或者值需要用户自定义销毁操作,则实现这个接口即可。</p>    <p>具体见下图</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/53b441fb52fda2c6f4c0a2c1eae15a4a.png"></p>    <p style="text-align:center">put.png</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/d61128c37efff44a76f0f45404ca71f8.png"></p>    <p style="text-align:center">get.png</p>    <h3><strong>三. 使用</strong></h3>    <pre>  <code class="language-java">/**   * Created by daiyiming on 2016/12/11.   * 缓存执行类   */  public final class MarkerMemoryCache {        private static MemoryCache<Key, Value> sLocalCache = new MemoryCache<>();        private static void confirmEnable() {          if (sLocalCache == null) {              sLocalCache = new MemoryCache<>();          }      }        public static void put(Key key, Value value) {          confirmEnable();          sLocalCache.put(key, value);      }        public static BitmapDescriptor get(Key key) {          confirmEnable();          Value value = sLocalCache.get(key);          if (value != null) {              return value.mBitmapDescriptor;          }          return null;      }        public static Key generateKey(int kind, String styleDetail, String styleId) {          return new Key(kind, styleDetail == null ? "" : styleDetail, styleId == null ? "" : styleId);      }        public static Value generateValue(BitmapDescriptor bitmapDescriptor) {          return new Value(bitmapDescriptor);      }        public static void clear() {          sLocalCache.clear();      }        public static final class Key {            private final int mKind;          private final String mStyleDetail;          private final String mStyleId;          private final int mHashCode;            private Key(int kind, String styleDetail, String styleId) {              mKind = kind;              mStyleDetail = styleDetail;              mStyleId = styleId;              mHashCode = generateHashCode();          }            private int generateHashCode() {              int result = 17;              result = 31 * result + mKind;              result = 31 * result + mStyleDetail.hashCode();              result = 31 * result + mStyleId.hashCode();              return result;          }            @Override          public boolean equals(Object object) {              if (object instanceof Key) {                  if (object == this) {                      return true;                  }                  Key key = (Key) object;                  return mKind == key.mKind                          && mStyleDetail.equals(key.mStyleDetail)                          && mStyleId.equals(key.mStyleId);              }              return false;          }            @Override          public int hashCode() {              return mHashCode;          }        }        public static final class Value implements MemoryCache.IRecycleInterface {            private BitmapDescriptor mBitmapDescriptor = null;            private Value(BitmapDescriptor bitmapDescriptor) {              mBitmapDescriptor = bitmapDescriptor;          }            @Override          public void recycle() {              if (mBitmapDescriptor != null) {                  Bitmap bitmap = mBitmapDescriptor.getBitmap();                  if (bitmap != null && !bitmap.isRecycled()) {                      bitmap.recycle();                  }              }          }      }    }</code></pre>    <p>这个实现是复杂实现,基本实现了所有功能,当然也可以简单使用~</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/7bc6d216ff25</p>    <p> </p>