Android簡單內存緩存

Earle96S 7年前發布 | 6K 次閱讀 緩存系統 Android開發 移動開發

一. 問題

前段時間在公司項目中遇到一個問題,在集成百度地圖時想要在地圖上動態添加 ICON,這些 ICON 都是隨時可變的,起初設計時,每次添加的 ICON 都是動態 new 出一個新的Bitmap,這樣可以滿足 ICON 隨時可變的需求,但是需求完成之后,通過 AS 觀察到 APP 內存飆升,APP 內存吃緊帶來的壞處我就不再贅述了,為了解決這個問題,我就設計了一個簡單的內存緩存框架來解決這個問題,有效的減少了 APP 的內存消耗。

二. 解決

解決問題時的問題思路我就不再說了,直接看下源碼~

/**

  • 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>

通過一個鏈表保存需要緩存的元素,每次添加的新元素放到鏈表的首部,添加時檢測如果超出最大容量,則從鏈表的尾部刪除一個元素,為什么這么做呢?主要是為了防止瘋狂 put 元素導致 OOM。在每次get元素的時候,通過傳入的 key 值遍歷鏈表,當命中想要獲取的元素的時候,將這個元素移到鏈表的首部,這樣可以保證頻繁獲取的元素在鏈表靠前的位置,減少獲取時間。在 get 成功的時候,如果鏈表長度大于容量(注意與最大容量的區別),我們就從列表末尾刪除一個元素,因為末尾的元素表明這個元素很大的可能是長時間沒人使用(get操作),即可視為過期元素。

為什么要設置一個容量和最大容量的區別呢?因為考慮到可能存在瘋狂 put 的操作,即用戶一直在 put,如果最大容量大于容量,就給了最先 put 的元素有被重新拿到鏈表首部的可能性,即一定程度的加強了這個元素被再次利用的可能性,在之后的 get 的操作中,鏈表尾部元素逐漸被清除,鏈表長度逐漸回歸正常。又因為設置了最大容量,給 put 操作設置了上限,所以基本不會有 OOM。

當然防止有些時候需要緩存的時候 IO 連接,SOCKET 連接,Bitmap 等需要手動銷毀的東西,我還實現了一個 Recycle 接口,如果緩存的鍵或者值需要用戶自定義銷毀操作,則實現這個接口即可。

具體見下圖

put.png

get.png

三. 使用

/**

  • 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>

這個實現是復雜實現,基本實現了所有功能,當然也可以簡單使用~

 

來自:http://www.jianshu.com/p/7bc6d216ff25

 

 本文由用戶 Earle96S 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!