深圳幻海软件技术有限公司 欢迎您!

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

2023-03-01

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。前言Android缓存机制:如果没有缓存,在大量的网络请求从远程获取图片时会造成网络流量的浪费,加载速度较慢,用户体验不好;今天我们就来聊聊Glide的缓存机制一、Glide中缓存

本文转载自微信公众号「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)方法。

@NonNull 
Glide build(@NonNull Context context) { 
  if (memoryCache == null) { 
    memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); 
  } 
  if (diskCacheFactory == null) { 
    diskCacheFactory = new InternalCacheDiskCacheFactory(context); 
  } 
  if (engine == null) { 
    engine = 
        new Engine( 
            memoryCache, 
            diskCacheFactory, 
            ...); 
  } 
  return new Glide( 
      ... 
      memoryCache, 
      ...); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

1、内存缓存-memoryCache

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

// Glide 
@Override 
public void onTrimMemory(int level) { 
  trimMemory(level); 

public void trimMemory(int level) { 
  // Engine asserts this anyway when removing resources, fail faster and consistently 
  Util.assertMainThread(); 
  // memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687. 
  memoryCache.trimMemory(level); 
  bitmapPool.trimMemory(level); 
  arrayPool.trimMemory(level); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

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接口定义。

public interface DiskCache { 
  interface Factory { 
    /** 250 MB of cache. */ 
    int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024; 
    String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache"
    @Nullable 
    DiskCache build(); 
  } 
  interface Writer { 
    boolean write(@NonNull File file); 
  } 
  @Nullable 
  File get(Key key); 
  void put(Key key, Writer writer); 
  @SuppressWarnings("unused"
  void delete(Key key); 
  void clear(); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

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

public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory { 
  public InternalCacheDiskCacheFactory(Context context) { 
    this(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, 
        DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE); 
  } 
  public InternalCacheDiskCacheFactory(Context context, long diskCacheSize) { 
    this(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, diskCacheSize); 
  } 
  public InternalCacheDiskCacheFactory(final Context context, final String diskCacheName, 
                                       long diskCacheSize) { 
    super(new CacheDirectoryGetter() { 
      @Override 
      public File getCacheDirectory() { 
        File cacheDirectory = context.getCacheDir(); 
        if (cacheDirectory == null) { 
          return null
        } 
        if (diskCacheName != null) { 
          return new File(cacheDirectory, diskCacheName); 
        } 
        return cacheDirectory; 
      } 
    }, diskCacheSize); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

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

继续看其父类DiskLruCacheFactory的代码:

public class DiskLruCacheFactory implements DiskCache.Factory { 
  private final long diskCacheSize; 
  private final CacheDirectoryGetter cacheDirectoryGetter; 
  public interface CacheDirectoryGetter { 
    File getCacheDirectory(); 
  } 
  ... 
  public DiskLruCacheFactory(CacheDirectoryGetter cacheDirectoryGetter, long diskCacheSize) { 
    this.diskCacheSize = diskCacheSize; 
    this.cacheDirectoryGetter = cacheDirectoryGetter; 
  } 
  @Override 
  public DiskCache build() { 
    File cacheDir = cacheDirectoryGetter.getCacheDirectory(); 
    if (cacheDir == null) { 
      return null
    } 
    if (!cacheDir.mkdirs() && (!cacheDir.exists() || !cacheDir.isDirectory())) { 
      return null
    } 
    return DiskLruCacheWrapper.create(cacheDir, diskCacheSize); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

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

public class DiskLruCacheWrapper implements DiskCache { 
  private static final String TAG = "DiskLruCacheWrapper"
  private static final int APP_VERSION = 1; 
  private static final int VALUE_COUNT = 1; 
  private static DiskLruCacheWrapper wrapper; 
  private final SafeKeyGenerator safeKeyGenerator; 
  private final File directory; 
  private final long maxSize; 
  private final DiskCacheWriteLocker writeLocker = new DiskCacheWriteLocker(); 
  private DiskLruCache diskLruCache; 
  @SuppressWarnings("deprecation"
  public static DiskCache create(File directory, long maxSize) { 
    return new DiskLruCacheWrapper(directory, maxSize); 
  } 
  @Deprecated 
  @SuppressWarnings({"WeakerAccess""DeprecatedIsStillUsed"}) 
  protected DiskLruCacheWrapper(File directory, long maxSize) { 
    this.directory = directory; 
    this.maxSize = maxSize; 
    this.safeKeyGenerator = new SafeKeyGenerator(); 
  } 
  private synchronized DiskLruCache getDiskCache() throws IOException { 
    if (diskLruCache == null) { 
      diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize); 
    } 
    return diskLruCache; 
  } 
  @Override 
  public File get(Key key) { 
    String safeKey = safeKeyGenerator.getSafeKey(key); 
    File result = null
    try { 
      final DiskLruCache.Value value = getDiskCache().get(safeKey); 
      if (value != null) { 
        result = value.getFile(0); 
      } 
    } catch (IOException e) { 
      ... 
    } 
    return result; 
  } 
  @Override 
  public void put(Key key, Writer writer) { 
    String safeKey = safeKeyGenerator.getSafeKey(key); 
    writeLocker.acquire(safeKey); 
    try { 
      try { 
        DiskLruCache diskCache = getDiskCache(); 
        Value current = diskCache.get(safeKey); 
        ... 
        DiskLruCache.Editor editor = diskCache.edit(safeKey); 
        ... 
        try { 
          File file = editor.getFile(0); 
          if (writer.write(file)) { 
            editor.commit(); 
          } 
        } finally { 
          editor.abortUnlessCommitted(); 
        } 
      } catch (IOException e) { 
        ... 
      } 
    } finally { 
      writeLocker.release(safeKey); 
    } 
  } 
  ... 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.

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

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

private static class LazyDiskCacheProvider implements DecodeJob.DiskCacheProvider { 
    private final DiskCache.Factory factory; 
    private volatile DiskCache diskCache; 
    LazyDiskCacheProvider(DiskCache.Factory factory) { 
      this.factory = factory; 
    } 
    ... 
    @Override 
    public DiskCache getDiskCache() { 
      if (diskCache == null) { 
        synchronized (this) { 
          if (diskCache == null) { 
            diskCache = factory.build(); 
          } 
          if (diskCache == null) { 
            diskCache = new DiskCacheAdapter(); 
          } 
        } 
      } 
      return diskCache; 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 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。

final class ActiveResources { 
  private final boolean isActiveResourceRetentionAllowed; 
  private final Executor monitorClearedResourcesExecutor; 
  @VisibleForTesting 
  final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>(); 
  private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>(); 
  private volatile boolean isShutdown; 
  ActiveResources(boolean isActiveResourceRetentionAllowed) { 
    this( 
        isActiveResourceRetentionAllowed, 
        java.util.concurrent.Executors.newSingleThreadExecutor( 
            new ThreadFactory() { 
              @Override 
              public Thread newThread(@NonNull final Runnable r) { 
                return new Thread( 
                    new Runnable() { 
                      @Override 
                      public void run() { 
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 
                        r.run(); 
                      } 
                    }, 
                    "glide-active-resources"); 
              } 
            })); 
  } 
  @VisibleForTesting 
  ActiveResources( 
      boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) { 
    this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed; 
    this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor; 
    monitorClearedResourcesExecutor.execute
        new Runnable() { 
          @Override 
          public void run() { 
            cleanReferenceQueue(); 
          } 
        }); 
  } 
  @SuppressWarnings("WeakerAccess"
  @Synthetic void cleanReferenceQueue() { 
    while (!isShutdown) { 
      try { 
        ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove(); 
        cleanupActiveReference(ref); 
        // This section for testing only
        DequeuedResourceCallback current = cb; 
        if (current != null) { 
          current.onResourceDequeued(); 
        } 
        // End for testing only
      } catch (InterruptedException e) { 
        Thread.currentThread().interrupt(); 
      } 
    } 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.

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

synchronized void activate(Key key, EngineResource<?> resource) { 
    ResourceWeakReference toPut = 
        new ResourceWeakReference( 
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed); 
    ResourceWeakReference removed = activeEngineResources.put(key, toPut); 
    if (removed != null) { 
      removed.reset(); 
    } 
  } 
  synchronized void deactivate(Key key) { 
    ResourceWeakReference removed = activeEngineResources.remove(key); 
    if (removed != null) { 
      removed.reset(); 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

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

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(); 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

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

void cleanupActiveReference(@NonNull ResourceWeakReference ref) { 
    synchronized (listener) { 
      synchronized (this) { 
        // 移除active资源 
        activeEngineResources.remove(ref.key); 
        if (!ref.isCacheable || ref.resource == null) { 
          return
        } 
        // 构造新的 Resource 
        EngineResource<?> newResource = 
            new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false); 
        newResource.setResourceListener(ref.key, listener); 
        // 回调Engine的onResourceReleased方法 
        // 这会导致此资源从active变成memory cache状态 
        listener.onResourceReleased(ref.key, newResource); 
      } 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

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

@Override 
  public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) { 
    activeResources.deactivate(cacheKey); 
    if (resource.isCacheable()) { 
      cache.put(cacheKey, resource); 
    } else { 
      resourceRecycler.recycle(resource); 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

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

4、磁盘缓存读取

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

public synchronized <R> LoadStatus load(...) { 
  EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, 
      resourceClass, transcodeClass, options); 
  EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); 
  if (active != null) { 
    cb.onResourceReady(active, DataSource.MEMORY_CACHE); 
    return null
  } 
  EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); 
  if (cached != null) { 
    cb.onResourceReady(cached, DataSource.MEMORY_CACHE); 
    return null
  } 
  EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); 
  if (current != null) { 
    current.addCallback(cb, callbackExecutor); 
    return new LoadStatus(cb, current); 
  } 
  EngineJob<R> engineJob = 
      engineJobFactory.build(...); 
  DecodeJob<R> decodeJob = 
      decodeJobFactory.build(...); 
  jobs.put(key, engineJob); 
  engineJob.addCallback(cb, callbackExecutor); 
  engineJob.start(decodeJob); 
  return new LoadStatus(cb, engineJob); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

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

EngineKey( 
      Object model, 
      Key signature, 
      int width 
      int height, 
      Map<Class<?>, Transformation<?>> transformations, 
      Class<?> resourceClass, 
      Class<?> transcodeClass, 
      Options options) 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

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中,否则回收掉该资源,代码如下:

public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) { 
    // 从activeResources中移除 
    activeResources.deactivate(cacheKey); 
    if (resource.isCacheable()) { 
      // 存入 MemoryCache 
      cache.put(cacheKey, resource); 
    } else { 
      resourceRecycler.recycle(resource); 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

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

@Nullable 
  private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) { 
    // 设置skipMemoryCache(true),则isMemoryCacheable为false,跳过ActiveResources 
    if (!isMemoryCacheable) { 
      return null
    } 
    EngineResource<?> active = activeResources.get(key); 
    if (active != null) { 
      // 命中缓存,引用计数+1 
      active.acquire(); 
    } 
    return active; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

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

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { 
    // 设置skipMemoryCache(true),则isMemoryCacheable为false,跳过ActiveResources 
    if (!isMemoryCacheable) { 
      return null
    } 
    EngineResource<?> cached = getEngineResourceFromCache(key); 
    if (cached != null) { 
      // 命中缓存,引用计数+1 
      cached.acquire(); 
      // 将此资源从memoryCache中移到activeResources中 
      activeResources.activate(key, cached); 
    } 
    return cached; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

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

先看下 ResourceCacheGenerator.startNext。

@Override 
  public boolean startNext() { 
    // list里面只有一个GlideUrl对象 
    List<Key> sourceIds = helper.getCacheKeys(); 
    if (sourceIds.isEmpty()) { 
      return false
    } 
    // 获得了三个可以到达的registeredResourceClasses 
    // GifDrawable、Bitmap、BitmapDrawable 
    List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses(); 
    if (resourceClasses.isEmpty()) { 
      if (File.class.equals(helper.getTranscodeClass())) { 
        return false
      } 
      throw new IllegalStateException( 
         "Failed to find any load path from " + helper.getModelClass() + " to " 
             + helper.getTranscodeClass()); 
    } 
    // 遍历sourceIds中的每一个key、resourceClasses中每一个class,以及其他的一些值组成key 
    // 尝试在磁盘缓存中以key找到缓存文件 
    while (modelLoaders == null || !hasNextModelLoader()) { 
      resourceClassIndex++; 
      if (resourceClassIndex >= resourceClasses.size()) { 
        sourceIdIndex++; 
        if (sourceIdIndex >= sourceIds.size()) { 
          return false
        } 
        resourceClassIndex = 0; 
      } 
      Key sourceId = sourceIds.get(sourceIdIndex); 
      Class<?> resourceClass = resourceClasses.get(resourceClassIndex); 
      Transformation<?> transformation = helper.getTransformation(resourceClass); 
      // PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway, 
      // we only run until the first one succeeds, the loop runs for only a limited 
      // number of iterations on the order of 10-20 in the worst case
      // 构造key 
      currentKey = 
          new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops 
              helper.getArrayPool(), 
              sourceId, 
              helper.getSignature(), 
              helper.getWidth(), 
              helper.getHeight(), 
              transformation, 
              resourceClass, 
              helper.getOptions()); 
      // 查找缓存文件 
      cacheFile = helper.getDiskCache().get(currentKey); 
      // 如果找到了缓存文件,循环条件则会为false,退出循环 
      if (cacheFile != null) { 
        sourceKey = sourceId; 
        // 1. 找出注入时以File.class为modelClass的注入代码 
        // 2. 调用所有注入的factory.build方法得到ModelLoader 
        // 3 .过滤掉不可能处理model的ModelLoader 
        // 此时的modelLoaders值为: 
        // [ByteBufferFileLoader, FileLoader, FileLoader, UnitModelLoader] 
        modelLoaders = helper.getModelLoaders(cacheFile); 
        modelLoaderIndex = 0; 
      } 
    } 
    // 如果找到了缓存文件,hasNextModelLoader()方法则会为true,可以执行循环 
    // 没有找到缓存文件,则不会进入循环,会直接返回false 
    loadData = null
    boolean started = false
    while (!started && hasNextModelLoader()) { 
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++); 
      // 在循环中会依次判断某个ModelLoader能不能加载此文件 
      loadData = modelLoader.buildLoadData(cacheFile, 
          helper.getWidth(), helper.getHeight(), helper.getOptions()); 
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { 
        started = true
        // 如果某个ModelLoader可以,那么就调用其fetcher进行加载数据        
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
// 加载成功或失败会通知自身 
        loadData.fetcher.loadData(helper.getPriority(), this); 
      } 
    } 
    return started; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

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

currentKey = 
          new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops 
              helper.getArrayPool(), 
              sourceId, 
              helper.getSignature(), 
              helper.getWidth(), 
              helper.getHeight(), 
              transformation, 
              resourceClass, 
              helper.getOptions()); 
ResourceCacheKey( 
      ArrayPool arrayPool, 
      Key sourceKey, 
      Key signature, 
      int width, 
      int height, 
      Transformation<?> appliedTransformation, 
      Class<?> decodedResourceClass, 
      Options options) 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

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方法。

@Override 
  public File get(Key key) { 
    String safeKey = safeKeyGenerator.getSafeKey(key); 
    ... 
    File result = null
    try { 
      final DiskLruCache.Value value = getDiskCache().get(safeKey); 
      if (value != null) { 
        result = value.getFile(0); 
      } 
    } catch (IOException e) { 
      ... 
    } 
    return result; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 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。

public void onDataReady(Object data) { 
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, 
        currentKey); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.

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

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

看下DataCacheGenerator.startNext:

public boolean startNext() { 
    while (modelLoaders == null || !hasNextModelLoader()) { 
      sourceIdIndex++; 
      if (sourceIdIndex >= cacheKeys.size()) { 
        return false
      } 
      Key sourceId = cacheKeys.get(sourceIdIndex); 
      ... 
      Key originalKey = new DataCacheKey(sourceId, helper.getSignature()); 
      cacheFile = helper.getDiskCache().get(originalKey); 
      ... 
    while (!started && hasNextModelLoader()) { 
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++); 
      loadData = 
          modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), 
              helper.getOptions()); 
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { 
        started = true
        loadData.fetcher.loadData(helper.getPriority(), this); 
      } 
    } 
    return started; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

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

DataCacheKey(Key sourceKey, Key signature)

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

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

@Override 
  public boolean startNext() { 
    // 首次运行dataToCache为null 
    if (dataToCache != null) { 
      Object data = dataToCache; 
      dataToCache = null
      cacheData(data); 
    } 
    // 首次运行sourceCacheGenerator为null 
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { 
      return true
    } 
    sourceCacheGenerator = null
    loadData = null
    boolean started = false
    while (!started && hasNextModelLoader()) { 
      loadData = helper.getLoadData().get(loadDataListIndex++); 
      if (loadData != null 
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) 
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { 
        started = true
        loadData.fetcher.loadData(helper.getPriority(), this); 
      } 
    } 
    return started; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

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

@Override 
  public void onDataReady(Object data) { 
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); 
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { 
      dataToCache = data; 
      // cb 为 DecodeJob 
      cb.reschedule(); 
    } else { 
      // cb 为 DecodeJob 
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, 
          loadData.fetcher.getDataSource(), originalKey); 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

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

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

private void cacheData(Object dataToCache) { 
    long startTime = LogTime.getLogTime(); 
    try { 
      Encoder<Object> encoder = helper.getSourceEncoder(dataToCache); 
      DataCacheWriter<Object> writer = 
          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions()); 
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature()); 
      helper.getDiskCache().put(originalKey, writer); 
      ... 
    } finally { 
      loadData.fetcher.cleanup(); 
    } 
    sourceCacheGenerator = 
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

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

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

// DecodeJob 
  @Override 
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, 
      DataSource dataSource, Key attemptedKey) { 
    this.currentSourceKey = sourceKey; 
    this.currentData = data; 
    this.currentFetcher = fetcher; 
    this.currentDataSource = dataSource; 
    this.currentAttemptingKey = attemptedKey; 
    if (Thread.currentThread() != currentThread) { 
      runReason = RunReason.DECODE_DATA; 
      callback.reschedule(this); 
    } else { 
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData"); 
      try { 
        decodeFromRetrievedData(); 
      } finally { 
        GlideTrace.endSection(); 
      } 
    } 
  } 
  private void decodeFromRetrievedData() { 
    ... 
    Resource<R> resource = null
    try { 
      resource = decodeFromData(currentFetcher, currentData, currentDataSource); 
    } catch (GlideException e) { 
      e.setLoggingDetails(currentAttemptingKey, currentDataSource); 
      throwables.add(e); 
    } 
    if (resource != null) { 
      notifyEncodeAndRelease(resource, currentDataSource); 
    } else { 
      runGenerators(); 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

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

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

@Synthetic 
  @NonNull 
  <Z> Resource<Z> onResourceDecoded(DataSource dataSource, 
      @NonNull Resource<Z> decoded) { 
    @SuppressWarnings("unchecked"
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass(); 
    Transformation<Z> appliedTransformation = null
    Resource<Z> transformed = decoded; 
    // 不是 resource cache时要transform 
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) { 
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass); 
      transformed = appliedTransformation.transform(glideContext, decoded, width, height); 
    } 
    // TODO: Make this the responsibility of the Transformation. 
    if (!decoded.equals(transformed)) { 
      decoded.recycle(); 
    } 
    final EncodeStrategy encodeStrategy; 
    final ResourceEncoder<Z> encoder; 
    if (decodeHelper.isResourceEncoderAvailable(transformed)) { 
      encoder = decodeHelper.getResultEncoder(transformed); 
      encodeStrategy = encoder.getEncodeStrategy(options); 
    } else { 
      encoder = null
      encodeStrategy = EncodeStrategy.NONE; 
    } 
    Resource<Z> result = transformed; 
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey); 
    if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource, 
        encodeStrategy)) { 
      if (encoder == null) { 
        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass()); 
      } 
      final Key key
      switch (encodeStrategy) { 
        case SOURCE: 
          key = new DataCacheKey(currentSourceKey, signature); 
          break; 
        case TRANSFORMED: 
          key = 
              new ResourceCacheKey( 
                  decodeHelper.getArrayPool(), 
                  currentSourceKey, 
                  signature, 
                  width, 
                  height, 
                  appliedTransformation, 
                  resourceSubClass, 
                  options); 
          break; 
        default
          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy); 
      } 
      LockedResource<Z> lockedResult = LockedResource.obtain(transformed); 
      deferredEncodeManager.init(key, encoder, lockedResult); 
      result = lockedResult; 
    } 
    return result; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.

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

public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() { 
        public boolean isDataCacheable(DataSource dataSource) { 
            return dataSource == DataSource.REMOTE; 
        } 
        public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) { 
            return (isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE || dataSource == DataSource.LOCAL) && encodeStrategy == EncodeStrategy.TRANSFORMED; 
        } 
        public boolean decodeCachedResource() { 
            return true
        } 
        public boolean decodeCachedData() { 
            return true
        } 
    }; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 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对象进行磁盘缓存的写入;

private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) { 
    ... 
    // 通知回调,资源已经就绪 
    notifyComplete(result, dataSource); 
    stage = Stage.ENCODE; 
    try { 
      if (deferredEncodeManager.hasResourceToEncode()) { 
        deferredEncodeManager.encode(diskCacheProvider, options); 
      } 
    } finally { 
      if (lockedResource != null) { 
        lockedResource.unlock(); 
      } 
    } 
    onEncodeComplete(); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

deferredEncodeManager.encode行磁盘缓存的写入。

// DecodeJob 
private static class DeferredEncodeManager<Z> { 
  private Key key
  private ResourceEncoder<Z> encoder; 
  private LockedResource<Z> toEncode; 
  @Synthetic 
  DeferredEncodeManager() { } 
  // We just need the encoder and resource type to match, which this will enforce. 
  @SuppressWarnings("unchecked"
  <X> void init(Key key, ResourceEncoder<X> encoder, LockedResource<X> toEncode) { 
    this.key = key
    this.encoder = (ResourceEncoder<Z>) encoder; 
    this.toEncode = (LockedResource<Z>) toEncode; 
  } 
  void encode(DiskCacheProvider diskCacheProvider, Options options) { 
    GlideTrace.beginSection("DecodeJob.encode"); 
    try { 
      // 存入磁盘缓存 
      diskCacheProvider.getDiskCache().put(key
          new DataCacheWriter<>(encoder, toEncode, options)); 
    } finally { 
      toEncode.unlock(); 
      GlideTrace.endSection(); 
    } 
  } 
  boolean hasResourceToEncode() { 
    return toEncode != null
  } 
  void clear() { 
    key = null
    encoder = null
    toEncode = null
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

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。

@Override 
  public synchronized void onEngineJobComplete( 
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) { 
    // 设置资源的回调为自己,这样在资源释放时会通知自己的回调方法 
    if (resource != null) { 
      resource.setResourceListener(key, this); 
      // 将资源放入activeResources中,资源变为active状态 
      if (resource.isCacheable()) { 
        activeResources.activate(key, resource); 
      } 
    } 
    // 将engineJob从Jobs中移除 
    jobs.removeIfCurrent(key, engineJob); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

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

@Synthetic 
void notifyCallbacksOfResult() { 
  ResourceCallbacksAndExecutors copy; 
  Key localKey; 
  EngineResource<?> localResource; 
  synchronized (this) { 
    ... 
    engineResource = engineResourceFactory.build(resource, isCacheable); 
    ... 
    hasResource = true
    copy = cbs.copy(); 
    incrementPendingCallbacks(copy.size() + 1); 
    localKey = key
    localResource = engineResource; 
  } 
  listener.onEngineJobComplete(this, localKey, localResource); 
  for (final ResourceCallbackAndExecutor entry : copy) { 
    entry.executor.execute(new CallResourceReady(entry.cb)); 
  } 
  decrementPendingCallbacks(); 

synchronized void incrementPendingCallbacks(int count) { 
  ... 
  if (pendingCallbacks.getAndAdd(count) == 0 && engineResource != null) { 
    engineResource.acquire(); 
  } 

synchronized void decrementPendingCallbacks() { 
  ... 
  int decremented = pendingCallbacks.decrementAndGet(); 
  if (decremented == 0) { 
    if (engineResource != null) { 
      engineResource.release(); 
    } 
    release(); 
  } 

private class CallResourceReady implements Runnable { 
  private final ResourceCallback cb; 
  CallResourceReady(ResourceCallback cb) { 
    this.cb = cb; 
  } 
  @Override 
  public void run() { 
    synchronized (EngineJob.this) { 
      if (cbs.contains(cb)) { 
        // Acquire for this particular callback. 
        engineResource.acquire(); 
        callCallbackOnResourceReady(cb); 
        removeCallback(cb); 
      } 
      decrementPendingCallbacks(); 
    } 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.

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

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