Stories

Detail Return Return

Android經典面試題之Glide的緩存大揭秘 - Stories Detail

本文首發於公眾號“AntDream”,歡迎微信搜索“AntDream”或掃描文章底部二維碼關注,和我一起每天進步一點點

Glide緩存

關聯類:Engine、LruResourceCache、LruCache、ActiveResources
ActiveResources:弱引用緩存池
@VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
LruCache:LinkedHashMap緩存池
private final Map<T, Entry<Y>> cache = new LinkedHashMap<>(100, 0.75f, true);

入口:Engine.load方法

先從緩存中取

--> loadFromMemory

--> loadFromActiveResources(弱引用池) 

--> loadFromCache 
--> getEngineResourceFromCache(LRU緩存池,LinkedHashMap)  

LRU緩存池中取到EngineResource後,會從LRU緩存中刪除,然後對它引用計數+1,放入弱引用池

緩存中沒有找到,就需要創建任務執行

-->  waitForExistingOrStartNewJob  
--> 如果當前圖片任務已經有EngineJob了,就直接加個Callback

---> 沒有的話就創建EngineJob和DecodeJob從本地加載或者是網絡加載

EngineResource通過引用計數來判斷是否需要釋放資源,釋放的資源會從弱引用池中刪除,放入LRU緩存中

緩存大小設置

涉及的類:MemorySizeCalculator
  • 首先獲取App可用內存大小,Glide的內存大小限制在0.4以下,如果是低內存的系統,則是在0.33
private static int getMaxSize(
    ActivityManager activityManager, float maxSizeMultiplier, float lowMemoryMaxSizeMultiplier) {
    final int memoryClassBytes = activityManager.getMemoryClass() * 1024 * 1024;
    final boolean isLowMemoryDevice = isLowMemoryDevice(activityManager);
    //lowMemoryMaxSizeMultiplier是0.33
    //maxSizeMultiplier是0.4
    return Math.round(
            memoryClassBytes * (isLowMemoryDevice ? lowMemoryMaxSizeMultiplier : maxSizeMultiplier));
}

@TargetApi(Build.VERSION_CODES.KITKAT)
@Synthetic
static boolean isLowMemoryDevice(ActivityManager activityManager) {
    if (Build.VERSION.SDK\_INT >= Build.VERSION\_CODES.KITKAT) {
        return activityManager.isLowRamDevice();
    } else {
        return true;
    }
}
  • 圖片緩存大小,用幾屏來表示,跟屏幕的分辨率有關
//一屏的圖片大小 寬*高*4(ARG888圖片的像素大小就是4字節)
int screenSize = widthPixels * heightPixels * BYTES_PER_ARGB_8888_PIXEL;

//BitmapPool最新的是API26以上是4,以下的是1
int targetBitmapPoolSize = Math.round(screenSize * builder.bitmapPoolScreens);

//memoryCache是2屏
int targetMemoryCacheSize = Math.round(screenSize * builder.memoryCacheScreens);
  • LRU緩存動態限制圖片緩存大小
 //在低內存回調,或是put新的圖片後,都會進行緩存大小檢查,如果超過就移除不太用的
protected synchronized void trimToSize(long size) {
    Map.Entry<T, Entry<Y>> last;
    Iterator<Map.Entry<T, Entry<Y>>> cacheIterator;
    while (currentSize > size) {
        cacheIterator = cache.entrySet().iterator();
        last = cacheIterator.next();
        final Entry\<Y> toRemove = last.getValue();
        currentSize -= toRemove.size;
        final T key = last.getKey();
        cacheIterator.remove();
        onItemEvicted(key, toRemove.value);
    }
}

DiskLruCache中的讀寫鎖

寫的時候會加鎖,這個鎖是自定義的,並且有一個鎖的池子

 private static class WriteLock {
   final Lock lock = new ReentrantLock();
   int interestedThreads;

   @Synthetic
   WriteLock() {}
}

每次寫的時候會加鎖,並且會對這個WriteLock的interestedThreads分別在開始寫和結束時進行加減操作

writeLocker.acquire(safeKey);
....
writeLocker.release(safeKey);

writeLocker從鎖池子裏取,key的話是用請求的key做哈希得到

 //SafeKeyGenerator.class
 private String calculateHexStringDigest(Key key) {
     PoolableDigestContainer container = Preconditions.checkNotNull(digestPool.acquire());
     try {
       key.updateDiskCacheKey(container.messageDigest);
       // calling digest() will automatically reset()
       return Util.sha256BytesToHex(container.messageDigest.digest());
     } finally {
       digestPool.release(container);
     }
}

鎖的緩存做了2級,一級是通過上面的key和鎖放在一個HashMap中;

//DiskCacheWriteLocker.class

private final Map<String, WriteLock> locks = new HashMap<>();
private final WriteLockPool writeLockPool = new WriteLockPool();

另一級是定義在內部類WriteLockPool的ArrayDeque裏面,默認大小是10

 private static class WriteLockPool {
    private static final int MAX_POOL_SIZE = 10;
    private final Queue<WriteLock> pool = new ArrayDeque<>();
    ...
}

取的時候先從HashMap中取,取不到再從WriteLockPool中取

void acquire(String safeKey) {
    WriteLock writeLock;
    synchronized (this) {
      writeLock = locks.get(safeKey);
      if (writeLock == null) {
        writeLock = writeLockPool.obtain();
        locks.put(safeKey, writeLock);
      }
      writeLock.interestedThreads++;
    }

    writeLock.lock.lock();
}

釋放鎖的時候,會把writeLock的interestedThreads進行減一操作,如果為0了就釋放鎖,放入WriteLockPool中

void release(String safeKey) {
    WriteLock writeLock;
    synchronized (this) {
      writeLock = Preconditions.checkNotNull(locks.get(safeKey));
      if (writeLock.interestedThreads < 1) {
        ...
      }

      writeLock.interestedThreads--;
      if (writeLock.interestedThreads == 0) {
        WriteLock removed = locks.remove(safeKey);
        ...
        writeLockPool.offer(removed);
      }
    }

    writeLock.lock.unlock();
}

歡迎關注我的公眾號AntDream查看更多精彩文章!

user avatar u_16502039 Avatar febobo Avatar wu_cat Avatar jkdataapi Avatar binghe001 Avatar flydean Avatar yangrd Avatar javaedge Avatar bluemoon_5a8f99b8431ab Avatar xiongshihubao Avatar immerse Avatar benfangdechaofen Avatar
Favorites 14 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.