Glide缓存原理

mac2024-07-28  53

Glide使用了DiskCache+MemoryCache+activeResources三级缓存 DiskCache是磁盘缓存,这个不用多说了 MemoryCache和activeResources是两级内存缓存 MemoryCache存储当前没有使用到的Bitmap,当MemoryCache中命中Bitmap后,该Bitmap又被缓存至activeResources中,并从MemoryCache中删除

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null; } EngineResource<?> cached = getEngineResourceFromCache(key); if (cached != null) { cached.acquire(); activeResources.activate(key, cached); } return cached; } private EngineResource<?> getEngineResourceFromCache(Key key) { Resource<?> cached = cache.remove(key); final EngineResource<?> result; if (cached == null) { result = null; } else if (cached instanceof EngineResource) { // Save an object allocation if we've cached an EngineResource (the typical case). result = (EngineResource<?>) cached; } else { result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/); } return result; }

activeResources可以理解为高速缓存,存储当前正在使用的Bitmap,并且使用weakReference+ReferenceQueue的方式保存,发生Gc的时候,又被缓存至MemoryCache中

下面是ActiveResources类

package com.bumptech.glide.load.engine; import android.os.Handler; import android.os.Handler.Callback; import android.os.Looper; import android.os.Message; import android.os.Process; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import com.bumptech.glide.load.Key; import com.bumptech.glide.load.engine.EngineResource.ResourceListener; import com.bumptech.glide.util.Preconditions; import com.bumptech.glide.util.Synthetic; import com.bumptech.glide.util.Util; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; final class ActiveResources { private static final int MSG_CLEAN_REF = 1; private final boolean isActiveResourceRetentionAllowed; private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == MSG_CLEAN_REF) { cleanupActiveReference((ResourceWeakReference) msg.obj); return true; } return false; } }); @VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>(); private ResourceListener listener; /** * Lazily instantiate to avoid exceptions if Glide is initialized on a background thread. * * @see <a href="https://github.com/bumptech/glide/issues/295">#295</a> */ @Nullable private ReferenceQueue<EngineResource<?>> resourceReferenceQueue; @Nullable private Thread cleanReferenceQueueThread; private volatile boolean isShutdown; @Nullable private volatile DequeuedResourceCallback cb; ActiveResources(boolean isActiveResourceRetentionAllowed) { this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed; } void setListener(ResourceListener listener) { this.listener = listener; } void activate(Key key, EngineResource<?> resource) { ResourceWeakReference toPut = new ResourceWeakReference( key, resource, getReferenceQueue(), isActiveResourceRetentionAllowed); ResourceWeakReference removed = activeEngineResources.put(key, toPut); if (removed != null) { removed.reset(); } } void deactivate(Key key) { ResourceWeakReference removed = activeEngineResources.remove(key); if (removed != null) { removed.reset(); } } @Nullable EngineResource<?> get(Key key) { ResourceWeakReference activeRef = activeEngineResources.get(key); if (activeRef == null) { return null; } EngineResource<?> active = activeRef.get(); if (active == null) { cleanupActiveReference(activeRef); } return active; } @SuppressWarnings("WeakerAccess") @Synthetic void cleanupActiveReference(@NonNull ResourceWeakReference ref) { Util.assertMainThread(); activeEngineResources.remove(ref.key); if (!ref.isCacheable || ref.resource == null) { return; } EngineResource<?> newResource = new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false); newResource.setResourceListener(ref.key, listener); listener.onResourceReleased(ref.key, newResource); } private ReferenceQueue<EngineResource<?>> getReferenceQueue() { if (resourceReferenceQueue == null) { resourceReferenceQueue = new ReferenceQueue<>(); cleanReferenceQueueThread = new Thread(new Runnable() { @SuppressWarnings("InfiniteLoopStatement") @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); cleanReferenceQueue(); } }, "glide-active-resources"); cleanReferenceQueueThread.start(); } return resourceReferenceQueue; } @SuppressWarnings("WeakerAccess") @Synthetic void cleanReferenceQueue() { while (!isShutdown) { try { ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove(); mainHandler.obtainMessage(MSG_CLEAN_REF, ref).sendToTarget(); // This section for testing only. DequeuedResourceCallback current = cb; if (current != null) { current.onResourceDequeued(); } // End for testing only. } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } @VisibleForTesting void setDequeuedResourceCallback(DequeuedResourceCallback cb) { this.cb = cb; } @VisibleForTesting interface DequeuedResourceCallback { void onResourceDequeued(); } @VisibleForTesting void shutdown() { isShutdown = true; if (cleanReferenceQueueThread == null) { return; } cleanReferenceQueueThread.interrupt(); try { cleanReferenceQueueThread.join(TimeUnit.SECONDS.toMillis(5)); if (cleanReferenceQueueThread.isAlive()) { throw new RuntimeException("Failed to join in time"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } @VisibleForTesting static final class ResourceWeakReference extends WeakReference<EngineResource<?>> { @SuppressWarnings("WeakerAccess") @Synthetic final Key key; @SuppressWarnings("WeakerAccess") @Synthetic final boolean isCacheable; @Nullable @SuppressWarnings("WeakerAccess") @Synthetic Resource<?> resource; @Synthetic @SuppressWarnings("WeakerAccess") ResourceWeakReference( @NonNull Key key, @NonNull EngineResource<?> referent, @NonNull ReferenceQueue<? super EngineResource<?>> queue, boolean isActiveResourceRetentionAllowed) { super(referent, queue); this.key = Preconditions.checkNotNull(key); this.resource = referent.isCacheable() && isActiveResourceRetentionAllowed ? Preconditions.checkNotNull(referent.getResource()) : null; isCacheable = referent.isCacheable(); } void reset() { resource = null; clear(); } } }

首先看activate方法,这里调用getReferenceQueue方法创建了ReferenceQueue用于绑定弱引用,并且起了一个线程,这个线程不断扫描resourceReferenceQueue,如果队列中有引用,根据ReferenceQueue(Leakcanary中也使用了)的特性,说明发生了GC,这个时候会进入到Handler中处理MSG_CLEAN_REF消息,最后执行到cleanupActiveReference方法中,执行了listener.onResourceReleased(ref.key, newResource);这里进入到Engine类中,执行方法onResourceReleased:

@Override public void onResourceReleased(Key cacheKey, EngineResource<?> resource) { Util.assertMainThread(); activeResources.deactivate(cacheKey); if (resource.isCacheable()) { cache.put(cacheKey, resource); } else { resourceRecycler.recycle(resource); } }

将Bitmap又放进了MemoryCache中去。 至于为什么这么设计,个人理解为MemoryCache缓存的数据可能会比较多,而activeResources只存储当前正在使用的Bitmap,数据存储量比较小,可以很快的进行存取,可以理解为高速缓存,当不被使用的时候又被放入MemoryCache中。 参考文献: 深入探究Glide的缓存机制 https://blog.csdn.net/guolin_blog/article/details/54895665 Glide如何通过引用计数复用Bitmap https://www.jianshu.com/p/00540c9a4de9

最新回复(0)