前言

Android缓存机制:如果没有缓存,在大量的网络请求从远程获取图片时会造成网络流量的浪费,加载速度较慢,用户体验不好;

今天我们就来聊聊Glide的缓存机制

一、Glide中缓存概念简述

Glide将它分成了两个模块,一个是内存缓存,一个是硬盘缓存;

1、内存缓存

内存缓存又分为两级,一级是LruCache缓存,一级是弱引用缓存

内存缓存的作用:防止应用重复将图片数据读取到内存当中。

LruCache缓存:不在使用中的图片使用LruCache来进行缓存。

弱引用缓存:把正在使用中的图片使用弱引用来进行缓存,这样的目的保护正在使用的资源不会被LruCache算法回收。

2、硬盘缓存

硬盘缓存的作用:防止应用重复从网络或其他地方重复下载和读取数据;

3、图片请求步骤

开始一个新的图片请求之前检查以下多级的缓存:

内存缓存:该图片是否最近被加载过并仍存在于内存中?即LruCache缓存;

活动资源:现在是否有另一个 View 正在展示这张图片?也就是弱引用缓存;

资源类型:该图片是否之前曾被解码、转换并写入过磁盘缓存?

数据来源:构建这个图片的资源是否之前曾被写入过文件缓存?

前两步检查图片是否在内存中,如果是则直接返回图片。后两步则检查图片是否在磁盘上,以便快速但异步地返回图片;

如果四个步骤都未能找到图片,则Glide会返回到原始资源以取回数据(原始文件,Uri, Url等);

图片存的顺序是:弱引用、内存、磁盘;

图片取的顺序是:内存、弱引用、磁盘;

4、Glide中Bitmap复用机制

Bitmap复用机制:将已经不需要使用的数据空间重新拿来使用,减少内存抖动(指在短时间内有大量的对象被创建或者被回收的现象);

BitmapFactory.Options.inMutable是Glide能够复用Bitmap的基石,是BitmapFactory提供的一个参数,表示该Bitmap是可变的,支持复用的。BitmapFactory.Options中提供了两个属性:inMutable、inBitmap。当进行Bitmap复用时,需要设置inMutable为true,inBitmap设置被复用的已经存在的Bitmap。Bitmap复用池使用LRU算法实现;

二、缓存源码流程

memory cache和disk cache在Glide创建的时候也被创建了,Glide创建的代码在GlideBuilder.build(Context)方法

  1. @NonNull 

  2. Glide build(@NonNull Context context) { 

  3.   if (memoryCache == null) { 

  4.     memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); 

  5.   } 

  6.   if (diskCacheFactory == null) { 

  7.     diskCacheFactory = new InternalCacheDiskCacheFactory(context); 

  8.   } 

  9.   if (engine == null) { 

  10.     engine = 

  11.         new Engine( 

  12.             memoryCache, 

  13.             diskCacheFactory, 

  14.             ...); 

  15.   } 

  16.   return new Glide( 

  17.       ... 

  18.       memoryCache, 

  19.       ...); 

1、内存缓存-memoryCache

通过代码可以看到 memoryCache 被放入 Engine 和 Glide 实例中。在Engine中利用memoryCache进行存取操作,Glide 实例中的memoryCache是用来在内存紧张的时候,通知memoryCache释放内存。Glide实现了ComponentCallbacks2接口,在Glide创建完成后,通过applicationContext.registerComponentCallbacks(glide)似的 Glide 实例可以监听内存紧张的信号。

  1. // Glide 

  2. @Override 

  3. public void onTrimMemory(int level) { 

  4.   trimMemory(level); 

  5. public void trimMemory(int level) { 

  6.   // Engine asserts this anyway when removing resources, fail faster and consistently 

  7.   Util.assertMainThread(); 

  8.   // memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687. 

  9.   memoryCache.trimMemory(level); 

  10.   bitmapPool.trimMemory(level); 

  11.   arrayPool.trimMemory(level); 

memoryCache是一个使用LRU(least recently used)算法实现的内存缓存类LruResourceCache,继承至LruCache类,并实现了MemoryCache接口。LruCache定义了LRU算法实现相关的操作,而MemoryCache定义的是内存缓存相关的操作。

LruCache 的实现是利用了 LinkedHashMap 的这种数据结构的一个特性( accessOrder=true 基于访问顺序 )再加上对 LinkedHashMap 的数据操作上锁实现的缓存策略。

当调用 put()方法时,就会在集合中添加元素,并调用

trimToSize()判断缓存是否已满,如果满了就用 LinkedHashMap 的迭代器删除队尾元素,即近期最少访问的元素。

当调用 get()方法访问缓存对象时,就会调用 LinkedHashMap 的 get()方法获得对应集合元素,同时会更新该元素到队头

2、磁盘缓存

diskCacheFactory是创建DiskCache的Factory,DiskCache接口定义

  1. public interface DiskCache { 

  2.   interface Factory { 

  3.     /** 250 MB of cache. */ 

  4.     int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024; 

  5.     String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache"

  6.     @Nullable 

  7.     DiskCache build(); 

  8.   } 

  9.   interface Writer { 

  10.     boolean write(@NonNull File file); 

  11.   } 

  12.   @Nullable 

  13.   File get(Key key); 

  14.   void put(Key key, Writer writer); 

  15.   @SuppressWarnings("unused"

  16.   void delete(Key key); 

  17.   void clear(); 

接着再来看下DiskCache.Factory的默认实现:InternalCacheDiskCacheFactory

  1. public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory { 

  2.   public InternalCacheDiskCacheFactory(Context context) { 

  3.     this(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, 

  4.         DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE); 

  5.   } 

  6.   public InternalCacheDiskCacheFactory(Context context, long diskCacheSize) { 

  7.     this(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, diskCacheSize); 

  8.   } 

  9.   public InternalCacheDiskCacheFactory(final Context context, final String diskCacheName, 

  10.                                        long diskCacheSize) { 

  11.     super(new CacheDirectoryGetter() { 

  12.       @Override 

  13.       public File getCacheDirectory() { 

  14.         File cacheDirectory = context.getCacheDir(); 

  15.         if (cacheDirectory == null) { 

  16.           return null

  17.         } 

  18.         if (diskCacheName != null) { 

  19.           return new File(cacheDirectory, diskCacheName); 

  20.         } 

  21.         return cacheDirectory; 

  22.       } 

  23.     }, diskCacheSize); 

  24.   } 

由以上代码可以看出:默认会创建一个250M的缓存目录,其路径为/data/data/{package}/cache/image_manager_disk_cache/

继续看其父类DiskLruCacheFactory的代码

  1. public class DiskLruCacheFactory implements DiskCache.Factory { 

  2.   private final long diskCacheSize; 

  3.   private final CacheDirectoryGetter cacheDirectoryGetter; 

  4.   public interface CacheDirectoryGetter { 

  5.     File getCacheDirectory(); 

  6.   } 

  7.   ... 

  8.   public DiskLruCacheFactory(CacheDirectoryGetter cacheDirectoryGetter, long diskCacheSize) { 

  9.     this.diskCacheSize = diskCacheSize; 

  10.     this.cacheDirectoryGetter = cacheDirectoryGetter; 

  11.   } 

  12.   @Override 

  13.   public DiskCache build() { 

  14.     File cacheDir = cacheDirectoryGetter.getCacheDirectory(); 

  15.     if (cacheDir == null) { 

  16.       return null

  17.     } 

  18.     if (!cacheDir.mkdirs() && (!cacheDir.exists() || !cacheDir.isDirectory())) { 

  19.       return null

  20.     } 

  21.     return DiskLruCacheWrapper.create(cacheDir, diskCacheSize); 

  22.   } 

DiskLruCacheFactory.build()方法会返回一个DiskLruCacheWrapper类的实例,看下DiskLruCacheWrapper的实现

  1. public class DiskLruCacheWrapper implements DiskCache { 

  2.   private static final String TAG = "DiskLruCacheWrapper"

  3.   private static final int APP_VERSION = 1; 

  4.   private static final int VALUE_COUNT = 1; 

  5.   private static DiskLruCacheWrapper wrapper; 

  6.   private final SafeKeyGenerator safeKeyGenerator; 

  7.   private final File directory; 

  8.   private final long maxSize; 

  9.   private final DiskCacheWriteLocker writeLocker = new DiskCacheWriteLocker(); 

  10.   private DiskLruCache diskLruCache; 

  11.   @SuppressWarnings("deprecation"

  12.   public static DiskCache create(File directory, long maxSize) { 

  13.     return new DiskLruCacheWrapper(directory, maxSize); 

  14.   } 

  15.   @Deprecated 

  16.   @SuppressWarnings({"WeakerAccess""DeprecatedIsStillUsed"}) 

  17.   protected DiskLruCacheWrapper(File directory, long maxSize) { 

  18.     this.directory = directory; 

  19.     this.maxSize = maxSize; 

  20.     this.safeKeyGenerator = new SafeKeyGenerator(); 

  21.   } 

  22.   private synchronized DiskLruCache getDiskCache() throws IOException { 

  23.     if (diskLruCache == null) { 

  24.       diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize); 

  25.     } 

  26.     return diskLruCache; 

  27.   } 

  28.   @Override 

  29.   public File get(Key key) { 

  30.     String safeKey = safeKeyGenerator.getSafeKey(key); 

  31.     File result = null

  32.     try { 

  33.       final DiskLruCache.Value value = getDiskCache().get(safeKey); 

  34.       if (value != null) { 

  35.         result = value.getFile(0); 

  36.       } 

  37.     } catch (IOException e) { 

  38.       ... 

  39.     } 

  40.     return result; 

  41.   } 

  42.   @Override 

  43.   public void put(Key key, Writer writer) { 

  44.     String safeKey = safeKeyGenerator.getSafeKey(key); 

  45.     writeLocker.acquire(safeKey); 

  46.     try { 

  47.       try { 

  48.         DiskLruCache diskCache = getDiskCache(); 

  49.         Value current = diskCache.get(safeKey); 

  50.         ... 

  51.         DiskLruCache.Editor editor = diskCache.edit(safeKey); 

  52.         ... 

  53.         try { 

  54.           File file = editor.getFile(0); 

  55.           if (writer.write(file)) { 

  56.             editor.commit(); 

  57.           } 

  58.         } finally { 

  59.           editor.abortUnlessCommitted(); 

  60.         } 

  61.       } catch (IOException e) { 

  62.         ... 

  63.       } 

  64.     } finally { 

  65.       writeLocker.release(safeKey); 

  66.     } 

  67.   } 

  68.   ... 

里面包装了一个DiskLruCache,该类主要是为DiskLruCache提供了一个根据Key生成safeKey的SafeKeyGenerator以及写锁DiskCacheWriteLocker。

回到GlideBuilder.build(Context)中,diskCacheFactory会被传进Engine中,在Engine的构造方法中会被包装成为一个LazyDiskCacheProvider,在被需要的时候调用getDiskCache()方法,这样就会调用factory的build()方法返回一个DiskCache。代码如下:

  1. private static class LazyDiskCacheProvider implements DecodeJob.DiskCacheProvider { 

  2.     private final DiskCache.Factory factory; 

  3.     private volatile DiskCache diskCache; 

  4.     LazyDiskCacheProvider(DiskCache.Factory factory) { 

  5.       this.factory = factory; 

  6.     } 

  7.     ... 

  8.     @Override 

  9.     public DiskCache getDiskCache() { 

  10.       if (diskCache == null) { 

  11.         synchronized (this) { 

  12.           if (diskCache == null) { 

  13.             diskCache = factory.build(); 

  14.           } 

  15.           if (diskCache == null) { 

  16.             diskCache = new DiskCacheAdapter(); 

  17.           } 

  18.         } 

  19.       } 

  20.       return diskCache; 

  21.     } 

  22.   } 

LazyDiskCacheProvider会在Engine后面的初始化流程中作为入参传到DecodeJobFactory的构造器。在DecodeJobFactory创建DecodeJob时也会作为入参会传进去,DecodeJob中会以全局变量保存此LazyDiskCacheProvider,在资源加载完毕并展示后,会进行缓存的存储。同时,DecodeJob也会在DecodeHelper初始化时,将此DiskCacheProvider设置进去,供ResourceCacheGenerator、DataCacheGenerator读取缓存,供SourceGenerator写入缓存

3、 ActiveResources

ActiveResources在Engine的构造器中被创建,在ActiveResources的构造器中会启动一个后台优先级级别(THREAD_PRIORITY_BACKGROUND)的线程,在该线程中会调用cleanReferenceQueue()方法一直循环清除ReferenceQueue中的将要被GC的Resource。

  1. final class ActiveResources { 

  2.   private final boolean isActiveResourceRetentionAllowed; 

  3.   private final Executor monitorClearedResourcesExecutor; 

  4.   @VisibleForTesting 

  5.   final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>(); 

  6.   private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>(); 

  7.   private volatile boolean isShutdown; 

  8.   ActiveResources(boolean isActiveResourceRetentionAllowed) { 

  9.     this( 

  10.         isActiveResourceRetentionAllowed, 

  11.         java.util.concurrent.Executors.newSingleThreadExecutor( 

  12.             new ThreadFactory() { 

  13.               @Override 

  14.               public Thread newThread(@NonNull final Runnable r) { 

  15.                 return new Thread( 

  16.                     new Runnable() { 

  17.                       @Override 

  18.                       public void run() { 

  19.                         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 

  20.                         r.run(); 

  21.                       } 

  22.                     }, 

  23.                     "glide-active-resources"); 

  24.               } 

  25.             })); 

  26.   } 

  27.   @VisibleForTesting 

  28.   ActiveResources( 

  29.       boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) { 

  30.     this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed; 

  31.     this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor; 

  32.     monitorClearedResourcesExecutor.execute

  33.         new Runnable() { 

  34.           @Override 

  35.           public void run() { 

  36.             cleanReferenceQueue(); 

  37.           } 

  38.         }); 

  39.   } 

  40.   @SuppressWarnings("WeakerAccess"

  41.   @Synthetic void cleanReferenceQueue() { 

  42.     while (!isShutdown) { 

  43.       try { 

  44.         ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove(); 

  45.         cleanupActiveReference(ref); 

  46.         // This section for testing only

  47.         DequeuedResourceCallback current = cb; 

  48.         if (current != null) { 

  49.           current.onResourceDequeued(); 

  50.         } 

  51.         // End for testing only

  52.       } catch (InterruptedException e) { 

  53.         Thread.currentThread().interrupt(); 

  54.       } 

  55.     } 

  56.   } 

先来看看ActiveResources的activate方法(保存)、deactivate方法(删除)的方法

  1. synchronized void activate(Key key, EngineResource<?> resource) { 

  2.     ResourceWeakReference toPut = 

  3.         new ResourceWeakReference( 

  4.             key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed); 

  5.     ResourceWeakReference removed = activeEngineResources.put(key, toPut); 

  6.     if (removed != null) { 

  7.       removed.reset(); 

  8.     } 

  9.   } 

  10.   synchronized void deactivate(Key key) { 

  11.     ResourceWeakReference removed = activeEngineResources.remove(key); 

  12.     if (removed != null) { 

  13.       removed.reset(); 

  14.     } 

  15.   } 

activate方法会将参数封装成为一个ResourceWeakReference,然后放入map中,如果对应的key之前有值,那么调用之前值的reset方法进行清除。deactivate方法先在map中移除,然后调用resource的reset方法进行清除。ResourceWeakReference继承WeakReference,内部只是保存了Resource的一些属性。

  1. static final class ResourceWeakReference extends WeakReference<EngineResource<?>> { 

  2.   @SuppressWarnings("WeakerAccess") @Synthetic final Key key

  3.   @SuppressWarnings("WeakerAccess") @Synthetic final boolean isCacheable; 

  4.   @Nullable @SuppressWarnings("WeakerAccess") @Synthetic Resource<?> resource; 

  5.   @Synthetic 

  6.   @SuppressWarnings("WeakerAccess"

  7.   ResourceWeakReference( 

  8.       @NonNull Key key

  9.       @NonNull EngineResource<?> referent, 

  10.       @NonNull ReferenceQueue<? super EngineResource<?>> queue, 

  11.       boolean isActiveResourceRetentionAllowed) { 

  12.     super(referent, queue); 

  13.     this.key = Preconditions.checkNotNull(key); 

  14.     this.resource = 

  15.         referent.isCacheable() && isActiveResourceRetentionAllowed 

  16.             ? Preconditions.checkNotNull(referent.getResource()) : null

  17.     isCacheable = referent.isCacheable(); 

  18.   } 

构造方法中调用了super(referent, queue),这样做可以让将要被GC的对象放入到ReferenceQueue中。而ActiveResources.cleanReferenceQueue()方法会一直尝试从queue中获取将要被GC的resource,然后调用cleanupActiveReference方法将resource从activeEngineResources中移除。cleanupActiveReference源码如下:

  1. void cleanupActiveReference(@NonNull ResourceWeakReference ref) { 

  2.     synchronized (listener) { 

  3.       synchronized (this) { 

  4.         // 移除active资源 

  5.         activeEngineResources.remove(ref.key); 

  6.         if (!ref.isCacheable || ref.resource == null) { 

  7.           return

  8.         } 

  9.         // 构造新的 Resource 

  10.         EngineResource<?> newResource = 

  11.             new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false); 

  12.         newResource.setResourceListener(ref.key, listener); 

  13.         // 回调Engine的onResourceReleased方法 

  14.         // 这会导致此资源从active变成memory cache状态 

  15.         listener.onResourceReleased(ref.key, newResource); 

  16.       } 

  17.     } 

  18.   } 

Engine实现了EngineResource.ResourceListener,此处的listener就是Engine,最终会回调Engine.onResourceReleased

  1. @Override 

  2.   public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) { 

  3.     activeResources.deactivate(cacheKey); 

  4.     if (resource.isCacheable()) { 

  5.       cache.put(cacheKey, resource); 

  6.     } else { 

  7.       resourceRecycler.recycle(resource); 

  8.     } 

  9.   } 

如果资源可以被缓存,则缓存到 memory cache,否则对资源进行回收

4、磁盘缓存读取

我们分析下缓存的存取代码。我们看下

  1. public synchronized <R> LoadStatus load(...) { 

  2.   EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, 

  3.       resourceClass, transcodeClass, options); 

  4.   EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); 

  5.   if (active != null) { 

  6.     cb.onResourceReady(active, DataSource.MEMORY_CACHE); 

  7.     return null

  8.   } 

  9.   EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); 

  10.   if (cached != null) { 

  11.     cb.onResourceReady(cached, DataSource.MEMORY_CACHE); 

  12.     return null

  13.   } 

  14.   EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); 

  15.   if (current != null) { 

  16.     current.addCallback(cb, callbackExecutor); 

  17.     return new LoadStatus(cb, current); 

  18.   } 

  19.   EngineJob<R> engineJob = 

  20.       engineJobFactory.build(...); 

  21.   DecodeJob<R> decodeJob = 

  22.       decodeJobFactory.build(...); 

  23.   jobs.put(key, engineJob); 

  24.   engineJob.addCallback(cb, callbackExecutor); 

  25.   engineJob.start(decodeJob); 

  26.   return new LoadStatus(cb, engineJob); 

缓存需要根据EngineKey去存取,先看下EngineKey的构造方法

  1. EngineKey( 

  2.       Object model, 

  3.       Key signature, 

  4.       int width 

  5.       int height, 

  6.       Map<Class<?>, Transformation<?>> transformations, 

  7.       Class<?> resourceClass, 

  8.       Class<?> transcodeClass, 

  9.       Options options) 

model:load方法传的参数;

signature:BaseRequestOptions的成员变量,默认会是EmptySignature.obtain()

在加载本地resource资源时会变成ApplicationVersionSignature.obtain(context);

width、height:如果没有指定override(int size),那么将得到view的size;

transformations:默认会基于ImageView的scaleType设置对应的四个Transformation;

如果指定了transform,那么就基于该值进行设置;

resourceClass:解码后的资源,如果没有asBitmap、asGif,一般会是Object;

transcodeClass:最终要转换成的数据类型,根据as方法确定,加载本地res或者网络URL,都会调用asDrawable,所以为Drawable

options:如果没有设置过transform,此处会根据ImageView的scaleType默认指定一个option;

所以,在多次加载同一个model的过程中,只要上述任何一个参数有改变,都不会认为是同一个key;

回到Engine.load方法,从缓存加载成功后的回调cb.onResourceReady(cached, DataSource.MEMORY_CACHE);可以看到:active状态的资源和memory cache状态的资源都是DataSource.MEMORY_CACHE,并且加载的资源都是 EngineResource 对象,该对象内部采用了引用计数去判断资源是否被释放,如果引用计数为0,那么会调用listener.onResourceReleased(key, this)方法通知外界此资源已经释放了。这里的listener是ResourceListener类型的接口,只有一个onResourceReleased(Key key, EngineResource resource)方法,Engine实现了该接口,此处的listener就是Engine。在Engine.onResourceReleased方法中会判断资源是否可缓存,可缓存则将此资源放入memory cache中,否则回收掉该资源,代码如下:

  1. public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) { 

  2.     // 从activeResources中移除 

  3.     activeResources.deactivate(cacheKey); 

  4.     if (resource.isCacheable()) { 

  5.       // 存入 MemoryCache 

  6.       cache.put(cacheKey, resource); 

  7.     } else { 

  8.       resourceRecycler.recycle(resource); 

  9.     } 

  10.   } 

继续回到Engine.load方法,先来看下active资源获取的方法

  1. @Nullable 

  2.   private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) { 

  3.     // 设置skipMemoryCache(true),则isMemoryCacheable为false,跳过ActiveResources 

  4.     if (!isMemoryCacheable) { 

  5.       return null

  6.     } 

  7.     EngineResource<?> active = activeResources.get(key); 

  8.     if (active != null) { 

  9.       // 命中缓存,引用计数+1 

  10.       active.acquire(); 

  11.     } 

  12.     return active; 

  13.   } 

继续分析cached资源获取的方法,如果从active资源中没有获取到缓存,则继续从内存缓存中查找

  1. private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { 

  2.     // 设置skipMemoryCache(true),则isMemoryCacheable为false,跳过ActiveResources 

  3.     if (!isMemoryCacheable) { 

  4.       return null

  5.     } 

  6.     EngineResource<?> cached = getEngineResourceFromCache(key); 

  7.     if (cached != null) { 

  8.       // 命中缓存,引用计数+1 

  9.       cached.acquire(); 

  10.       // 将此资源从memoryCache中移到activeResources中 

  11.       activeResources.activate(key, cached); 

  12.     } 

  13.     return cached; 

  14.   } 

如果从memoryCache中获取到资源则将此资源从memoryCache中移到activeResources中。第一次加载的时候activeResources和memoryCache中都没有缓存的,后面继续通过DecodeJob和EngineJob去加载资源。DecoceJob实现了Runnable接口,然后会被EngineJob.start方法提交到对应的线程池中去执行。在DecoceJob的run方法中,会依次从ResourceCacheGenerator和DataCacheGenerator中去取缓存数据,当这两者都取不到的情况下,会交给SourceGenerator加载网络图片或者本地资源。resource资源和data资源都是磁盘缓存中的资源。

先看下 ResourceCacheGenerator.startNext

  1. @Override 

  2.   public boolean startNext() { 

  3.     // list里面只有一个GlideUrl对象 

  4.     List<Key> sourceIds = helper.getCacheKeys(); 

  5.     if (sourceIds.isEmpty()) { 

  6.       return false

  7.     } 

  8.     // 获得了三个可以到达的registeredResourceClasses 

  9.     // GifDrawable、Bitmap、BitmapDrawable 

  10.     List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses(); 

  11.     if (resourceClasses.isEmpty()) { 

  12.       if (File.class.equals(helper.getTranscodeClass())) { 

  13.         return false

  14.       } 

  15.       throw new IllegalStateException( 

  16.          "Failed to find any load path from " + helper.getModelClass() + " to " 

  17.              + helper.getTranscodeClass()); 

  18.     } 

  19.     // 遍历sourceIds中的每一个key、resourceClasses中每一个class,以及其他的一些值组成key 

  20.     // 尝试在磁盘缓存中以key找到缓存文件 

  21.     while (modelLoaders == null || !hasNextModelLoader()) { 

  22.       resourceClassIndex++; 

  23.       if (resourceClassIndex >= resourceClasses.size()) { 

  24.         sourceIdIndex++; 

  25.         if (sourceIdIndex >= sourceIds.size()) { 

  26.           return false

  27.         } 

  28.         resourceClassIndex = 0; 

  29.       } 

  30.       Key sourceId = sourceIds.get(sourceIdIndex); 

  31.       Class<?> resourceClass = resourceClasses.get(resourceClassIndex); 

  32.       Transformation<?> transformation = helper.getTransformation(resourceClass); 

  33.       // PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway, 

  34.       // we only run until the first one succeeds, the loop runs for only a limited 

  35.       // number of iterations on the order of 10-20 in the worst case

  36.       // 构造key 

  37.       currentKey = 

  38.           new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops 

  39.               helper.getArrayPool(), 

  40.               sourceId, 

  41.               helper.getSignature(), 

  42.               helper.getWidth(), 

  43.               helper.getHeight(), 

  44.               transformation, 

  45.               resourceClass, 

  46.               helper.getOptions()); 

  47.       // 查找缓存文件 

  48.       cacheFile = helper.getDiskCache().get(currentKey); 

  49.       // 如果找到了缓存文件,循环条件则会为false,退出循环 

  50.       if (cacheFile != null) { 

  51.         sourceKey = sourceId; 

  52.         // 1. 找出注入时以File.class为modelClass的注入代码 

  53.         // 2. 调用所有注入的factory.build方法得到ModelLoader 

  54.         // 3 .过滤掉不可能处理model的ModelLoader 

  55.         // 此时的modelLoaders值为: 

  56.         // [ByteBufferFileLoader, FileLoader, FileLoader, UnitModelLoader] 

  57.         modelLoaders = helper.getModelLoaders(cacheFile); 

  58.         modelLoaderIndex = 0; 

  59.       } 

  60.     } 

  61.     // 如果找到了缓存文件,hasNextModelLoader()方法则会为true,可以执行循环 

  62.     // 没有找到缓存文件,则不会进入循环,会直接返回false 

  63.     loadData = null

  64.     boolean started = false

  65.     while (!started && hasNextModelLoader()) { 

  66.       ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++); 

  67.       // 在循环中会依次判断某个ModelLoader能不能加载此文件 

  68.       loadData = modelLoader.buildLoadData(cacheFile, 

  69.           helper.getWidth(), helper.getHeight(), helper.getOptions()); 

  70.       if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { 

  71.         started = true

  72.         // 如果某个ModelLoader可以,那么就调用其fetcher进行加载数据       

  1. // 加载成功或失败会通知自身 

  2.         loadData.fetcher.loadData(helper.getPriority(), this); 

  3.       } 

  4.     } 

  5.     return started; 

  6.   } 

该方法的相关注释代码里都有标明。找缓存时key的类型为ResourceCacheKey,我们先来看下ResourceCacheKey的构成

  1. currentKey = 

  2.           new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops 

  3.               helper.getArrayPool(), 

  4.               sourceId, 

  5.               helper.getSignature(), 

  6.               helper.getWidth(), 

  7.               helper.getHeight(), 

  8.               transformation, 

  9.               resourceClass, 

  10.               helper.getOptions()); 

  11. ResourceCacheKey( 

  12.       ArrayPool arrayPool, 

  13.       Key sourceKey, 

  14.       Key signature, 

  15.       int width, 

  16.       int height, 

  17.       Transformation<?> appliedTransformation, 

  18.       Class<?> decodedResourceClass, 

  19.       Options options) 

arrayPool:默认值是LruArrayPool,不参与key的equals方法;

sourceKey:如果请求的是URL,此处就是GlideUrl(GlideUrl implements Key);

signature:BaseRequestOptions的成员变量,默认会是EmptySignature.obtain(),

在加载本地resource资源时会变成ApplicationVersionSignature.obtain(context);

width、height:如果没有指定override(int size),那么将得到view的size;

appliedTransformation:默认会根据ImageView的scaleType设置对应的BitmapTransformation;

如果指定了transform,那么就会是指定的值;

decodedResourceClass:可以被编码成的资源类型,如BitmapDrawable等;

options:如果没有设置过transform,此处会根据ImageView的scaleType默认指定一个option;

在ResourceCacheKey中,arrayPool并没有参与equals方法;

生成ResourceCacheKey之后会根据key去磁盘缓存中查找cacheFile = helper.getDiskCache().get(currentKey);

helper.getDiskCache()返回DiskCache接口,它的实现类是DiskLruCacheWrapper,看下DiskLruCacheWrapper.get方法

  1. @Override 

  2.   public File get(Key key) { 

  3.     String safeKey = safeKeyGenerator.getSafeKey(key); 

  4.     ... 

  5.     File result = null

  6.     try { 

  7.       final DiskLruCache.Value value = getDiskCache().get(safeKey); 

  8.       if (value != null) { 

  9.         result = value.getFile(0); 

  10.       } 

  11.     } catch (IOException e) { 

  12.       ... 

  13.     } 

  14.     return result; 

  15.   } 

这里调用SafeKeyGenerator生成了一个String类型的SafeKey,实际上就是对原始key中每个字段都使用SHA-256加密,然后将得到的字节数组转换为16进制的字符串。生成SafeKey后,接着根据SafeKey去DiskCache里面找对应的缓存文件,然后返回文件。

回到ResourceCacheGenerator.startNext方法中,如果找到了缓存会调用loadData.fetcher.loadData(helper.getPriority(), this);这里的 fetcher 是 ByteBufferFetcher,ByteBufferFetcher的loadData方法中最终会执行callback.onDataReady(result)这里callback是ResourceCacheGenerator

  1. public void onDataReady(Object data) { 

  2.     cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, 

  3.         currentKey); 

  4.   } 

ResourceCacheGenerator的onDataReady方法又会回调DecodeJob的onDataFetcherReady方法进行后续的解码操作。

如果ResourceCacheGenerator没有找到缓存,就会交给DataCacheGenerator继续查找缓存。该类大体流程和ResourceCacheGenerator一样,有点不同的是,DataCacheGenerator的构造器有两个构造器,其中的DataCacheGenerator(List, DecodeHelper, FetcherReadyCallback)构造器是给SourceGenerator准备的。因为如果没有磁盘缓存,那么从源头加载后,肯定需要进行磁盘缓存操作的。所以,SourceGenerator会将加载后的资源保存到磁盘中,然后转交给DataCacheGenerator从磁盘中取出交给ImageView展示。

看下DataCacheGenerator.startNext

  1. public boolean startNext() { 

  2.     while (modelLoaders == null || !hasNextModelLoader()) { 

  3.       sourceIdIndex++; 

  4.       if (sourceIdIndex >= cacheKeys.size()) { 

  5.         return false

  6.       } 

  7.       Key sourceId = cacheKeys.get(sourceIdIndex); 

  8.       ... 

  9.       Key originalKey = new DataCacheKey(sourceId, helper.getSignature()); 

  10.       cacheFile = helper.getDiskCache().get(originalKey); 

  11.       ... 

  12.     while (!started && hasNextModelLoader()) { 

  13.       ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++); 

  14.       loadData = 

  15.           modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), 

  16.               helper.getOptions()); 

  17.       if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { 

  18.         started = true

  19.         loadData.fetcher.loadData(helper.getPriority(), this); 

  20.       } 

  21.     } 

  22.     return started; 

  23.   } 

这里的originalKey是DataCacheKey类型的,DataCacheKey构造方法如下

DataCacheKey(Key sourceKey, Key signature)

这里的sourceKey和signature与ResourceCacheKey中的两个变量一致,从这里就可以看出:DataCache缓存的是原始的数据,ResourceCache缓存的是是被解码、转换后的数据。

如果DataCacheGenerator没有取到缓存,那么会交给SourceGenerator从源头加载。看下SourceGenerator的startNext方法

  1. @Override 

  2.   public boolean startNext() { 

  3.     // 首次运行dataToCache为null 

  4.     if (dataToCache != null) { 

  5.       Object data = dataToCache; 

  6.       dataToCache = null

  7.       cacheData(data); 

  8.     } 

  9.     // 首次运行sourceCacheGenerator为null 

  10.     if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { 

  11.       return true

  12.     } 

  13.     sourceCacheGenerator = null

  14.     loadData = null

  15.     boolean started = false

  16.     while (!started && hasNextModelLoader()) { 

  17.       loadData = helper.getLoadData().get(loadDataListIndex++); 

  18.       if (loadData != null 

  19.           && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) 

  20.           || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { 

  21.         started = true

  22.         loadData.fetcher.loadData(helper.getPriority(), this); 

  23.       } 

  24.     } 

  25.     return started; 

  26.   } 

加载成功后依然会回调SourceGenerator的onDataReady方法

  1. @Override 

  2.   public void onDataReady(Object data) { 

  3.     DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); 

  4.     if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { 

  5.       dataToCache = data; 

  6.       // cb 为 DecodeJob 

  7.       cb.reschedule(); 

  8.     } else { 

  9.       // cb 为 DecodeJob 

  10.       cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, 

  11.           loadData.fetcher.getDataSource(), originalKey); 

  12.     } 

  13.   } 

先判断获取到的数据是否需要进行磁盘缓存,如果需要磁盘缓存,则经过DecodeJob、EngineJob的调度,重新调用SourceGenerator.startNext方法,此时dataToCache已经被赋值,则会调用cacheData(data);进行磁盘缓存的写入,并转交给DataCacheGenerator完成后续的处理;否则就通知DecodeJob已经加载成功。

先看下SourceGenerator的startNext方法中调用的SourceGenerator.cacheData(data)

  1. private void cacheData(Object dataToCache) { 

  2.     long startTime = LogTime.getLogTime(); 

  3.     try { 

  4.       Encoder<Object> encoder = helper.getSourceEncoder(dataToCache); 

  5.       DataCacheWriter<Object> writer = 

  6.           new DataCacheWriter<>(encoder, dataToCache, helper.getOptions()); 

  7.       originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature()); 

  8.       helper.getDiskCache().put(originalKey, writer); 

  9.       ... 

  10.     } finally { 

  11.       loadData.fetcher.cleanup(); 

  12.     } 

  13.     sourceCacheGenerator = 

  14.         new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this); 

  15.   } 

cacheData方法先构建了一个DataCacheKey将data写入了磁盘,然后new了一个DataCacheGenerator赋值给sourceCacheGenerator。回到startNext继续向下执行,此时sourceCacheGenerator不为空,就调用其startNext()方法从磁盘中加载刚写入磁盘的数据,并返回true让DecodeJob停止尝试获取数据。此时,从磁盘缓存中读取数据的逻辑已经完成,接下来是写磁盘缓存。

假如SourceGenerator的onDataReady方法中的磁盘缓存策略不可用,则会回调DecodeJob.onDataFetcherReady方法

  1. // DecodeJob 

  2.   @Override 

  3.   public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, 

  4.       DataSource dataSource, Key attemptedKey) { 

  5.     this.currentSourceKey = sourceKey; 

  6.     this.currentData = data; 

  7.     this.currentFetcher = fetcher; 

  8.     this.currentDataSource = dataSource; 

  9.     this.currentAttemptingKey = attemptedKey; 

  10.     if (Thread.currentThread() != currentThread) { 

  11.       runReason = RunReason.DECODE_DATA; 

  12.       callback.reschedule(this); 

  13.     } else { 

  14.       GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData"); 

  15.       try { 

  16.         decodeFromRetrievedData(); 

  17.       } finally { 

  18.         GlideTrace.endSection(); 

  19.       } 

  20.     } 

  21.   } 

  22.   private void decodeFromRetrievedData() { 

  23.     ... 

  24.     Resource<R> resource = null

  25.     try { 

  26.       resource = decodeFromData(currentFetcher, currentData, currentDataSource); 

  27.     } catch (GlideException e) { 

  28.       e.setLoggingDetails(currentAttemptingKey, currentDataSource); 

  29.       throwables.add(e); 

  30.     } 

  31.     if (resource != null) { 

  32.       notifyEncodeAndRelease(resource, currentDataSource); 

  33.     } else { 

  34.       runGenerators(); 

  35.     } 

  36.   } 

decodeFromRetrievedData();后续的方法调用链在之前的文章中分析过,主要做的事情就是:将原始的data数据转变为可以供ImageView显示的resource数据并将其显示在ImageView上。

将原始的data数据转变为resource数据后,会调用DecodeJob.onResourceDecoded(dataSource, decoded)

  1. @Synthetic 

  2.   @NonNull 

  3.   <Z> Resource<Z> onResourceDecoded(DataSource dataSource, 

  4.       @NonNull Resource<Z> decoded) { 

  5.     @SuppressWarnings("unchecked"

  6.     Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass(); 

  7.     Transformation<Z> appliedTransformation = null

  8.     Resource<Z> transformed = decoded; 

  9.     // 不是 resource cache时要transform 

  10.     if (dataSource != DataSource.RESOURCE_DISK_CACHE) { 

  11.       appliedTransformation = decodeHelper.getTransformation(resourceSubClass); 

  12.       transformed = appliedTransformation.transform(glideContext, decoded, width, height); 

  13.     } 

  14.     // TODO: Make this the responsibility of the Transformation. 

  15.     if (!decoded.equals(transformed)) { 

  16.       decoded.recycle(); 

  17.     } 

  18.     final EncodeStrategy encodeStrategy; 

  19.     final ResourceEncoder<Z> encoder; 

  20.     if (decodeHelper.isResourceEncoderAvailable(transformed)) { 

  21.       encoder = decodeHelper.getResultEncoder(transformed); 

  22.       encodeStrategy = encoder.getEncodeStrategy(options); 

  23.     } else { 

  24.       encoder = null

  25.       encodeStrategy = EncodeStrategy.NONE; 

  26.     } 

  27.     Resource<Z> result = transformed; 

  28.     boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey); 

  29.     if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource, 

  30.         encodeStrategy)) { 

  31.       if (encoder == null) { 

  32.         throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass()); 

  33.       } 

  34.       final Key key

  35.       switch (encodeStrategy) { 

  36.         case SOURCE: 

  37.           key = new DataCacheKey(currentSourceKey, signature); 

  38.           break; 

  39.         case TRANSFORMED: 

  40.           key = 

  41.               new ResourceCacheKey( 

  42.                   decodeHelper.getArrayPool(), 

  43.                   currentSourceKey, 

  44.                   signature, 

  45.                   width, 

  46.                   height, 

  47.                   appliedTransformation, 

  48.                   resourceSubClass, 

  49.                   options); 

  50.           break; 

  51.         default

  52.           throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy); 

  53.       } 

  54.       LockedResource<Z> lockedResult = LockedResource.obtain(transformed); 

  55.       deferredEncodeManager.init(key, encoder, lockedResult); 

  56.       result = lockedResult; 

  57.     } 

  58.     return result; 

  59.   } 

然后是此过程中的磁盘缓存过程,影响的因素有encodeStrategy、DiskCacheStrategy.isResourceCacheable。encodeStrategy根据resource数据的类型来判断,如果是Bitmap或BitmapDrawable,那么就是TRANSFORMED;如果是GifDrawable,那么就是SOURCE。磁盘缓存策略默认是DiskCacheStrategy.AUTOMATIC。源码如下:

  1. public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() { 

  2.         public boolean isDataCacheable(DataSource dataSource) { 

  3.             return dataSource == DataSource.REMOTE; 

  4.         } 

  5.         public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) { 

  6.             return (isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE || dataSource == DataSource.LOCAL) && encodeStrategy == EncodeStrategy.TRANSFORMED; 

  7.         } 

  8.         public boolean decodeCachedResource() { 

  9.             return true

  10.         } 

  11.         public boolean decodeCachedData() { 

  12.             return true

  13.         } 

  14.     }; 

只有dataSource为DataSource.LOCAL且encodeStrategy为EncodeStrategy.TRANSFORMED时,才允许缓存。也就是只有本地的resource数据为Bitmap或BitmapDrawable的资源才可以缓存。

在DecodeJob.onResourceDecoded中会调用deferredEncodeManager.init(key, encoder, lockedResult);去初始化deferredEncodeManager。

在DecodeJob的decodeFromRetrievedData();中拿到resource数据后会调用notifyEncodeAndRelease(resource, currentDataSource)利用deferredEncodeManager对象进行磁盘缓存的写入;

  1. private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) { 

  2.     ... 

  3.     // 通知回调,资源已经就绪 

  4.     notifyComplete(result, dataSource); 

  5.     stage = Stage.ENCODE; 

  6.     try { 

  7.       if (deferredEncodeManager.hasResourceToEncode()) { 

  8.         deferredEncodeManager.encode(diskCacheProvider, options); 

  9.       } 

  10.     } finally { 

  11.       if (lockedResource != null) { 

  12.         lockedResource.unlock(); 

  13.       } 

  14.     } 

  15.     onEncodeComplete(); 

  16.   } 

deferredEncodeManager.encode行磁盘缓存的写入

  1. // DecodeJob 

  2. private static class DeferredEncodeManager<Z> { 

  3.   private Key key

  4.   private ResourceEncoder<Z> encoder; 

  5.   private LockedResource<Z> toEncode; 

  6.   @Synthetic 

  7.   DeferredEncodeManager() { } 

  8.   // We just need the encoder and resource type to match, which this will enforce. 

  9.   @SuppressWarnings("unchecked"

  10.   <X> void init(Key key, ResourceEncoder<X> encoder, LockedResource<X> toEncode) { 

  11.     this.key = key

  12.     this.encoder = (ResourceEncoder<Z>) encoder; 

  13.     this.toEncode = (LockedResource<Z>) toEncode; 

  14.   } 

  15.   void encode(DiskCacheProvider diskCacheProvider, Options options) { 

  16.     GlideTrace.beginSection("DecodeJob.encode"); 

  17.     try { 

  18.       // 存入磁盘缓存 

  19.       diskCacheProvider.getDiskCache().put(key

  20.           new DataCacheWriter<>(encoder, toEncode, options)); 

  21.     } finally { 

  22.       toEncode.unlock(); 

  23.       GlideTrace.endSection(); 

  24.     } 

  25.   } 

  26.   boolean hasResourceToEncode() { 

  27.     return toEncode != null

  28.   } 

  29.   void clear() { 

  30.     key = null

  31.     encoder = null

  32.     toEncode = null

  33.   } 

diskCacheProvider.getDiskCache()获取到DiskLruCacheWrapper,并调用DiskLruCacheWrapper的put写入。DiskLruCacheWrapper在写入的时候会使用到写锁DiskCacheWriteLocker,锁对象由对象池WriteLockPool创建,写锁WriteLock实现是一个不公平锁ReentrantLock。

在缓存写入前,会判断key对应的value存不存在,若存在则不写入。缓存的真正写入会由DataCacheWriter交给ByteBufferEncoder和StreamEncoder两个具体类来写入,前者负责将ByteBuffer写入到文件,后者负责将InputStream写入到文件。

目前为止,磁盘缓存的读写流程都已分析完成;

5、内存缓存:ActiveResource与MemoryCache读取

回到DecodeJob.notifyEncodeAndRelease方法中,经过notifyComplete、EngineJob.onResourceReady、notifyCallbacksOfResult方法中。

在该方法中一方面会将原始的resource包装成一个EngineResource,然后通过回调传给Engine.onEngineJobComplete

  1. @Override 

  2.   public synchronized void onEngineJobComplete( 

  3.       EngineJob<?> engineJob, Key key, EngineResource<?> resource) { 

  4.     // 设置资源的回调为自己,这样在资源释放时会通知自己的回调方法 

  5.     if (resource != null) { 

  6.       resource.setResourceListener(key, this); 

  7.       // 将资源放入activeResources中,资源变为active状态 

  8.       if (resource.isCacheable()) { 

  9.         activeResources.activate(key, resource); 

  10.       } 

  11.     } 

  12.     // 将engineJob从Jobs中移除 

  13.     jobs.removeIfCurrent(key, engineJob); 

  14.   } 

在这里会将资源放入activeResources中,资源变为active状态。后面会使用Executors.mainThreadExecutor()调用SingleRequest.onResourceReady回调进行资源的显示。在触发回调前后各有一个地方会对engineResource进行acquire()和release()操作,这两个操作分别发生在notifyCallbacksOfResult()方法的incrementPendingCallbacks、decrementPendingCallbacks()调用中

  1. @Synthetic 

  2. void notifyCallbacksOfResult() { 

  3.   ResourceCallbacksAndExecutors copy; 

  4.   Key localKey; 

  5.   EngineResource<?> localResource; 

  6.   synchronized (this) { 

  7.     ... 

  8.     engineResource = engineResourceFactory.build(resource, isCacheable); 

  9.     ... 

  10.     hasResource = true

  11.     copy = cbs.copy(); 

  12.     incrementPendingCallbacks(copy.size() + 1); 

  13.     localKey = key

  14.     localResource = engineResource; 

  15.   } 

  16.   listener.onEngineJobComplete(this, localKey, localResource); 

  17.   for (final ResourceCallbackAndExecutor entry : copy) { 

  18.     entry.executor.execute(new CallResourceReady(entry.cb)); 

  19.   } 

  20.   decrementPendingCallbacks(); 

  21. synchronized void incrementPendingCallbacks(int count) { 

  22.   ... 

  23.   if (pendingCallbacks.getAndAdd(count) == 0 && engineResource != null) { 

  24.     engineResource.acquire(); 

  25.   } 

  26. synchronized void decrementPendingCallbacks() { 

  27.   ... 

  28.   int decremented = pendingCallbacks.decrementAndGet(); 

  29.   if (decremented == 0) { 

  30.     if (engineResource != null) { 

  31.       engineResource.release(); 

  32.     } 

  33.     release(); 

  34.   } 

  35. private class CallResourceReady implements Runnable { 

  36.   private final ResourceCallback cb; 

  37.   CallResourceReady(ResourceCallback cb) { 

  38.     this.cb = cb; 

  39.   } 

  40.   @Override 

  41.   public void run() { 

  42.     synchronized (EngineJob.this) { 

  43.       if (cbs.contains(cb)) { 

  44.         // Acquire for this particular callback. 

  45.         engineResource.acquire(); 

  46.         callCallbackOnResourceReady(cb); 

  47.         removeCallback(cb); 

  48.       } 

  49.       decrementPendingCallbacks(); 

  50.     } 

  51.   } 

CallResourceReady的run方法中也会调用engineResource.acquire(),上面的代码调用结束后,engineResource的引用计数为1。engineResource的引用计数会在RequestManager.onDestory方法中最终调用SingleRequest.clear()方法,SingleRequest.clear()内部调用releaseResource()、Engine.release 进行释放,这样引用计数就变为0。引用计数就变为0后会通知Engine将此资源从active状态变成memory cache状态。如果我们再次加载资源时可以从memory cache中加载,那么资源又会从memory cache状态变成active状态。也就是说,在资源第一次显示后,我们关闭页面,资源会由active变成memory cache;然后我们再次进入页面,加载时会命中memory cache,从而又变成active状态

总结

读取内存缓存时,先从LruCache算法机制的内存缓存读取,再从弱引用机制的内存缓存读取;

写入内存缓存时,先写入 弱引用机制 的内存缓存,等到图片不再被使用时,再写入到 LruCache算法机制的内存缓存;

读取磁盘缓存时,先读取转换后图片的缓存,再读取原始图片的缓存;