Android源码进阶之Glide缓存机制原理详解

移动开发 Android
Android缓存机制:如果没有缓存,在大量的网络请求从远程获取图片时会造成网络流量的浪费,加载速度较慢,用户体验不好;今天我们就来聊聊Glide的缓存机制。

[[420787]]

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。

前言

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算法机制的内存缓存; 

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

 

责任编辑:武晓燕 来源: Android开发编程
相关推荐

2021-09-03 07:27:38

AndroidGlide管理

2021-09-02 07:00:01

Glide流程Android

2021-09-09 06:55:43

AndroidViewDragHel原理

2021-09-04 07:29:57

Android

2021-09-07 06:40:25

AndroidLiveData原理

2021-10-15 09:19:17

AndroidSharedPrefe分析源码

2021-08-17 13:41:11

AndroidView事件

2021-09-14 07:06:12

Android磁盘缓存

2023-02-24 16:46:25

Glide缓存机制

2021-09-06 13:12:05

前端JavaScript编程

2011-06-23 14:05:32

Qt 事件机制

2021-08-05 20:39:34

AndroidKotlinStandard.kt

2021-09-11 07:32:15

Java线程线程池

2021-08-10 20:41:33

AndroidApp流程

2021-08-12 16:28:10

AndroidHandleLooper

2021-09-08 06:51:52

AndroidRetrofit原理

2011-06-23 13:10:39

Python 对象机制

2021-09-05 07:35:58

lifecycleAndroid组件原理

2021-09-12 07:30:10

配置

2021-09-30 07:36:51

AndroidViewDraw
点赞
收藏

51CTO技术栈公众号