Android源碼進(jìn)階之Glide緩存機(jī)制原理詳解

前言

Android緩存機(jī)制:如果沒有緩存,在大量的網(wǎng)絡(luò)請(qǐng)求從遠(yuǎn)程獲取圖片時(shí)會(huì)造成網(wǎng)絡(luò)流量的浪費(fèi),加載速度較慢,用戶體驗(yàn)不好;

今天我們就來聊聊Glide的緩存機(jī)制

B站100集視頻教程【Android源碼解析Retrofit/OkHttp/Glide/RxJava/EventBus....】:https://www.bilibili.com/video/BV1mT4y1R7F4?spm_id_from=333.999.0.0
一、Glide中緩存概念簡(jiǎn)述

Glide將它分成了兩個(gè)模塊,一個(gè)是內(nèi)存緩存,一個(gè)是硬盤緩存;

1、內(nèi)存緩存

  • 內(nèi)存緩存又分為兩級(jí),一級(jí)是LruCache緩存,一級(jí)是弱引用緩存
  • 內(nèi)存緩存的作用:防止應(yīng)用重復(fù)將圖片數(shù)據(jù)讀取到內(nèi)存當(dāng)中。
  • LruCache緩存:不在使用中的圖片使用LruCache來進(jìn)行緩存。
  • 弱引用緩存:把正在使用中的圖片使用弱引用來進(jìn)行緩存,這樣的目的保護(hù)正在使用的資源不會(huì)被LruCache算法回收。

2、硬盤緩存

硬盤緩存的作用:防止應(yīng)用重復(fù)從網(wǎng)絡(luò)或其他地方重復(fù)下載和讀取數(shù)據(jù);

3、圖片請(qǐng)求步驟

開始一個(gè)新的圖片請(qǐng)求之前檢查以下多級(jí)的緩存:

  • 內(nèi)存緩存:該圖片是否最近被加載過并仍存在于內(nèi)存中?即LruCache緩存;
  • 活動(dòng)資源:現(xiàn)在是否有另一個(gè) View 正在展示這張圖片?也就是弱引用緩存;
  • 資源類型:該圖片是否之前曾被解碼、轉(zhuǎn)換并寫入過磁盤緩存?
  • 數(shù)據(jù)來源:構(gòu)建這個(gè)圖片的資源是否之前曾被寫入過文件緩存?
  • 前兩步檢查圖片是否在內(nèi)存中,如果是則直接返回圖片。后兩步則檢查圖片是否在磁盤上,以便快速但異步地返回圖片;
  • 如果四個(gè)步驟都未能找到圖片,則Glide會(huì)返回到原始資源以取回?cái)?shù)據(jù)(原始文件,Uri, Url等);
  • 圖片存的順序是:弱引用、內(nèi)存、磁盤;
  • 圖片取的順序是:內(nèi)存、弱引用、磁盤;

4、Glide中Bitmap復(fù)用機(jī)制

  • Bitmap復(fù)用機(jī)制:將已經(jīng)不需要使用的數(shù)據(jù)空間重新拿來使用,減少內(nèi)存抖動(dòng)(指在短時(shí)間內(nèi)有大量的對(duì)象被創(chuàng)建或者被回收的現(xiàn)象);
  • BitmapFactory.Options.inMutable是Glide能夠復(fù)用Bitmap的基石,是BitmapFactory提供的一個(gè)參數(shù),表示該Bitmap是可變的,支持復(fù)用的。BitmapFactory.Options中提供了兩個(gè)屬性:inMutable、inBitmap。當(dāng)進(jìn)行Bitmap復(fù)用時(shí),需要設(shè)置inMutable為true,inBitmap設(shè)置被復(fù)用的已經(jīng)存在的Bitmap。Bitmap復(fù)用池使用LRU算法實(shí)現(xiàn);

二、緩存源碼流程

memory cache和disk cache在Glide創(chuàng)建的時(shí)候也被創(chuàng)建了,Glide創(chuàng)建的代碼在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、內(nèi)存緩存-memoryCache

通過代碼可以看到 memoryCache 被放入 Engine 和 Glide 實(shí)例中。在Engine中利用memoryCache進(jìn)行存取操作,Glide 實(shí)例中的memoryCache是用來在內(nèi)存緊張的時(shí)候,通知memoryCache釋放內(nèi)存。Glide實(shí)現(xiàn)了ComponentCallbacks2接口,在Glide創(chuàng)建完成后,通過applicationContext.registerComponentCallbacks(glide)似的 Glide 實(shí)例可以監(jiān)聽內(nèi)存緊張的信號(hào)。

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

memoryCache是一個(gè)使用LRU(least recently used)算法實(shí)現(xiàn)的內(nèi)存緩存類LruResourceCache,繼承至LruCache類,并實(shí)現(xiàn)了MemoryCache接口。LruCache定義了LRU算法實(shí)現(xiàn)相關(guān)的操作,而MemoryCache定義的是內(nèi)存緩存相關(guān)的操作。

LruCache 的實(shí)現(xiàn)是利用了 LinkedHashMap 的這種數(shù)據(jù)結(jié)構(gòu)的一個(gè)特性( accessOrder=true 基于訪問順序 )再加上對(duì) LinkedHashMap 的數(shù)據(jù)操作上鎖實(shí)現(xiàn)的緩存策略。

當(dāng)調(diào)用 put()方法時(shí),就會(huì)在集合中添加元素,并調(diào)用

trimToSize()判斷緩存是否已滿,如果滿了就用 LinkedHashMap 的迭代器刪除隊(duì)尾元素,即近期最少訪問的元素。

當(dāng)調(diào)用 get()方法訪問緩存對(duì)象時(shí),就會(huì)調(diào)用 LinkedHashMap 的 get()方法獲得對(duì)應(yīng)集合元素,同時(shí)會(huì)更新該元素到隊(duì)頭

2、磁盤緩存

diskCacheFactory是創(chuàng)建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();
}

接著再來看下DiskCache.Factory的默認(rèn)實(shí)現(xiàn):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);
  }
}

由以上代碼可以看出:默認(rèn)會(huì)創(chuàng)建一個(gè)250M的緩存目錄,其路徑為/data/data/{package}/cache/image_manager_disk_cache/

繼續(xù)看其父類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);
  }
}

DiskLruCacheFactory.build()方法會(huì)返回一個(gè)DiskLruCacheWrapper類的實(shí)例,看下DiskLruCacheWrapper的實(shí)現(xiàn)

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

里面包裝了一個(gè)DiskLruCache,該類主要是為DiskLruCache提供了一個(gè)根據(jù)Key生成safeKey的SafeKeyGenerator以及寫鎖DiskCacheWriteLocker。

回到GlideBuilder.build(Context)中,diskCacheFactory會(huì)被傳進(jìn)Engine中,在Engine的構(gòu)造方法中會(huì)被包裝成為一個(gè)LazyDiskCacheProvider,在被需要的時(shí)候調(diào)用getDiskCache()方法,這樣就會(huì)調(diào)用factory的build()方法返回一個(gè)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;
    }
  }

LazyDiskCacheProvider會(huì)在Engine后面的初始化流程中作為入?yún)鞯紻ecodeJobFactory的構(gòu)造器。在DecodeJobFactory創(chuàng)建DecodeJob時(shí)也會(huì)作為入?yún)?huì)傳進(jìn)去,DecodeJob中會(huì)以全局變量保存此LazyDiskCacheProvider,在資源加載完畢并展示后,會(huì)進(jìn)行緩存的存儲(chǔ)。同時(shí),DecodeJob也會(huì)在DecodeHelper初始化時(shí),將此DiskCacheProvider設(shè)置進(jìn)去,供ResourceCacheGenerator、DataCacheGenerator讀取緩存,供SourceGenerator寫入緩存

3、 ActiveResources

ActiveResources在Engine的構(gòu)造器中被創(chuàng)建,在ActiveResources的構(gòu)造器中會(huì)啟動(dòng)一個(gè)后臺(tái)優(yōu)先級(jí)級(jí)別(THREAD_PRIORITY_BACKGROUND)的線程,在該線程中會(huì)調(diào)用cleanReferenceQueue()方法一直循環(huán)清除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();
      }
    }
  }
}

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

activate方法會(huì)將參數(shù)封裝成為一個(gè)ResourceWeakReference,然后放入map中,如果對(duì)應(yīng)的key之前有值,那么調(diào)用之前值的reset方法進(jìn)行清除。deactivate方法先在map中移除,然后調(diào)用resource的reset方法進(jìn)行清除。ResourceWeakReference繼承WeakReference,內(nèi)部只是保存了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();
  }
}

構(gòu)造方法中調(diào)用了super(referent, queue),這樣做可以讓將要被GC的對(duì)象放入到ReferenceQueue中。而ActiveResources.cleanReferenceQueue()方法會(huì)一直嘗試從queue中獲取將要被GC的resource,然后調(diào)用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;
        }
        // 構(gòu)造新的 Resource
        EngineResource<?> newResource =
            new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
        newResource.setResourceListener(ref.key, listener);
        // 回調(diào)Engine的onResourceReleased方法
        // 這會(huì)導(dǎo)致此資源從active變成memory cache狀態(tài)
        listener.onResourceReleased(ref.key, newResource);
      }
    }
  }

Engine實(shí)現(xiàn)了EngineResource.ResourceListener,此處的listener就是Engine,最終會(huì)回調(diào)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);
    }
  }

如果資源可以被緩存,則緩存到 memory cache,否則對(duì)資源進(jìn)行回收

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

緩存需要根據(jù)EngineKey去存取,先看下EngineKey的構(gòu)造方法


EngineKey(
      Object model,
      Key signature,
      int width
      int height,
      Map<Class<?>, Transformation<?>> transformations,
      Class<?> resourceClass,
      Class<?> transcodeClass,
      Options options)
  • model:load方法傳的參數(shù);
  • signature:BaseRequestOptions的成員變量,默認(rèn)會(huì)是EmptySignature.obtain()
  • 在加載本地resource資源時(shí)會(huì)變成ApplicationVersionSignature.obtain(context);
  • width、height:如果沒有指定override(int size),那么將得到view的size;
  • transformations:默認(rèn)會(huì)基于ImageView的scaleType設(shè)置對(duì)應(yīng)的四個(gè)Transformation;
  • 如果指定了transform,那么就基于該值進(jìn)行設(shè)置;
  • resourceClass:解碼后的資源,如果沒有asBitmap、asGif,一般會(huì)是Object;
  • transcodeClass:最終要轉(zhuǎn)換成的數(shù)據(jù)類型,根據(jù)as方法確定,加載本地res或者網(wǎng)絡(luò)URL,都會(huì)調(diào)用asDrawable,所以為Drawable
  • options:如果沒有設(shè)置過transform,此處會(huì)根據(jù)ImageView的scaleType默認(rèn)指定一個(gè)option;
  • 所以,在多次加載同一個(gè)model的過程中,只要上述任何一個(gè)參數(shù)有改變,都不會(huì)認(rèn)為是同一個(gè)key;

回到Engine.load方法,從緩存加載成功后的回調(diào)cb.onResourceReady(cached, DataSource.MEMORY_CACHE);可以看到:active狀態(tài)的資源和memory cache狀態(tài)的資源都是DataSource.MEMORY_CACHE,并且加載的資源都是 EngineResource 對(duì)象,該對(duì)象內(nèi)部采用了引用計(jì)數(shù)去判斷資源是否被釋放,如果引用計(jì)數(shù)為0,那么會(huì)調(diào)用listener.onResourceReleased(key, this)方法通知外界此資源已經(jīng)釋放了。這里的listener是ResourceListener類型的接口,只有一個(gè)onResourceReleased(Key key, EngineResource<?> resource)方法,Engine實(shí)現(xiàn)了該接口,此處的listener就是Engine。在Engine.onResourceReleased方法中會(huì)判斷資源是否可緩存,可緩存則將此資源放入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);
    }
  }

繼續(xù)回到Engine.load方法,先來看下active資源獲取的方法

@Nullable
  private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    // 設(shè)置skipMemoryCache(true),則isMemoryCacheable為false,跳過ActiveResources
    if (!isMemoryCacheable) {
      return null;
    }
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      // 命中緩存,引用計(jì)數(shù)+1
      active.acquire();
    }
    return active;
  }

繼續(xù)分析cached資源獲取的方法,如果從active資源中沒有獲取到緩存,則繼續(xù)從內(nèi)存緩存中查找

 private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    // 設(shè)置skipMemoryCache(true),則isMemoryCacheable為false,跳過ActiveResources
    if (!isMemoryCacheable) {
      return null;
    }
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      // 命中緩存,引用計(jì)數(shù)+1
      cached.acquire();
      // 將此資源從memoryCache中移到activeResources中
      activeResources.activate(key, cached);
    }
    return cached;
  }

如果從memoryCache中獲取到資源則將此資源從memoryCache中移到activeResources中。第一次加載的時(shí)候activeResources和memoryCache中都沒有緩存的,后面繼續(xù)通過DecodeJob和EngineJob去加載資源。DecoceJob實(shí)現(xiàn)了Runnable接口,然后會(huì)被EngineJob.start方法提交到對(duì)應(yīng)的線程池中去執(zhí)行。在DecoceJob的run方法中,會(huì)依次從ResourceCacheGenerator和DataCacheGenerator中去取緩存數(shù)據(jù),當(dāng)這兩者都取不到的情況下,會(huì)交給SourceGenerator加載網(wǎng)絡(luò)圖片或者本地資源。resource資源和data資源都是磁盤緩存中的資源。

先看下 ResourceCacheGenerator.startNext

@Override
  public boolean startNext() {
    // list里面只有一個(gè)GlideUrl對(duì)象
    List<Key> sourceIds = helper.getCacheKeys();
    if (sourceIds.isEmpty()) {
      return false;
    }
    // 獲得了三個(gè)可以到達(dá)的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中的每一個(gè)key、resourceClasses中每一個(gè)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.
      // 構(gòu)造key
      currentKey =
          new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());
      // 查找緩存文件
      cacheFile = helper.getDiskCache().get(currentKey);
      // 如果找到了緩存文件,循環(huán)條件則會(huì)為false,退出循環(huán)
      if (cacheFile != null) {
        sourceKey = sourceId;
        // 1\. 找出注入時(shí)以File.class為modelClass的注入代碼
        // 2\. 調(diào)用所有注入的factory.build方法得到ModelLoader
        // 3 .過濾掉不可能處理model的ModelLoader
        // 此時(shí)的modelLoaders值為:
        // [ByteBufferFileLoader, FileLoader, FileLoader, UnitModelLoader]
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }
    // 如果找到了緩存文件,hasNextModelLoader()方法則會(huì)為true,可以執(zhí)行循環(huán)
    // 沒有找到緩存文件,則不會(huì)進(jìn)入循環(huán),會(huì)直接返回false
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
      // 在循環(huán)中會(huì)依次判斷某個(gè)ModelLoader能不能加載此文件
      loadData = modelLoader.buildLoadData(cacheFile,
          helper.getWidth(), helper.getHeight(), helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        // 如果某個(gè)ModelLoader可以,那么就調(diào)用其fetcher進(jìn)行加載數(shù)據(jù)       
// 加載成功或失敗會(huì)通知自身
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

該方法的相關(guān)注釋代碼里都有標(biāo)明。找緩存時(shí)key的類型為ResourceCacheKey,我們先來看下ResourceCacheKey的構(gòu)成


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)
  • arrayPool:默認(rèn)值是LruArrayPool,不參與key的equals方法;
  • sourceKey:如果請(qǐng)求的是URL,此處就是GlideUrl(GlideUrl implements Key);
  • signature:BaseRequestOptions的成員變量,默認(rèn)會(huì)是EmptySignature.obtain(),
  • 在加載本地resource資源時(shí)會(huì)變成ApplicationVersionSignature.obtain(context);
  • width、height:如果沒有指定override(int size),那么將得到view的size;
  • appliedTransformation:默認(rèn)會(huì)根據(jù)ImageView的scaleType設(shè)置對(duì)應(yīng)的BitmapTransformation;
  • 如果指定了transform,那么就會(huì)是指定的值;
  • decodedResourceClass:可以被編碼成的資源類型,如BitmapDrawable等;
  • options:如果沒有設(shè)置過transform,此處會(huì)根據(jù)ImageView的scaleType默認(rèn)指定一個(gè)option;

在ResourceCacheKey中,arrayPool并沒有參與equals方法;

生成ResourceCacheKey之后會(huì)根據(jù)key去磁盤緩存中查找cacheFile = helper.getDiskCache().get(currentKey);

helper.getDiskCache()返回DiskCache接口,它的實(shí)現(xiàn)類是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;
  }

這里調(diào)用SafeKeyGenerator生成了一個(gè)String類型的SafeKey,實(shí)際上就是對(duì)原始key中每個(gè)字段都使用SHA-256加密,然后將得到的字節(jié)數(shù)組轉(zhuǎn)換為16進(jìn)制的字符串。生成SafeKey后,接著根據(jù)SafeKey去DiskCache里面找對(duì)應(yīng)的緩存文件,然后返回文件。

回到ResourceCacheGenerator.startNext方法中,如果找到了緩存會(huì)調(diào)用loadData.fetcher.loadData(helper.getPriority(), this);這里的 fetcher 是 ByteBufferFetcher,ByteBufferFetcher的loadData方法中最終會(huì)執(zhí)行callback.onDataReady(result)這里callback是ResourceCacheGenerator

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

ResourceCacheGenerator的onDataReady方法又會(huì)回調(diào)DecodeJob的onDataFetcherReady方法進(jìn)行后續(xù)的解碼操作。

如果ResourceCacheGenerator沒有找到緩存,就會(huì)交給DataCacheGenerator繼續(xù)查找緩存。該類大體流程和ResourceCacheGenerator一樣,有點(diǎn)不同的是,DataCacheGenerator的構(gòu)造器有兩個(gè)構(gòu)造器,其中的DataCacheGenerator(List<key style="box-sizing: border-box;">, DecodeHelper<?>, FetcherReadyCallback)構(gòu)造器是給SourceGenerator準(zhǔn)備的。因?yàn)槿绻麤]有磁盤緩存,那么從源頭加載后,肯定需要進(jìn)行磁盤緩存操作的。所以,SourceGenerator會(huì)將加載后的資源保存到磁盤中,然后轉(zhuǎn)交給DataCacheGenerator從磁盤中取出交給ImageView展示。</key>

看下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;
  }

這里的originalKey是DataCacheKey類型的,DataCacheKey構(gòu)造方法如下

DataCacheKey(Key sourceKey, Key signature)

這里的sourceKey和signature與ResourceCacheKey中的兩個(gè)變量一致,從這里就可以看出:DataCache緩存的是原始的數(shù)據(jù),ResourceCache緩存的是是被解碼、轉(zhuǎn)換后的數(shù)據(jù)。

如果DataCacheGenerator沒有取到緩存,那么會(huì)交給SourceGenerator從源頭加載??聪耂ourceGenerator的startNext方法

@Override
  public boolean startNext() {
    // 首次運(yùn)行dataToCache為null
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }
    // 首次運(yùn)行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;
  }

加載成功后依然會(huì)回調(diào)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);
    }
  }

先判斷獲取到的數(shù)據(jù)是否需要進(jìn)行磁盤緩存,如果需要磁盤緩存,則經(jīng)過DecodeJob、EngineJob的調(diào)度,重新調(diào)用SourceGenerator.startNext方法,此時(shí)dataToCache已經(jīng)被賦值,則會(huì)調(diào)用cacheData(data);進(jìn)行磁盤緩存的寫入,并轉(zhuǎn)交給DataCacheGenerator完成后續(xù)的處理;否則就通知DecodeJob已經(jīng)加載成功。

先看下SourceGenerator的startNext方法中調(diào)用的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);
  }

cacheData方法先構(gòu)建了一個(gè)DataCacheKey將data寫入了磁盤,然后new了一個(gè)DataCacheGenerator賦值給sourceCacheGenerator?;氐絪tartNext繼續(xù)向下執(zhí)行,此時(shí)sourceCacheGenerator不為空,就調(diào)用其startNext()方法從磁盤中加載剛寫入磁盤的數(shù)據(jù),并返回true讓DecodeJob停止嘗試獲取數(shù)據(jù)。此時(shí),從磁盤緩存中讀取數(shù)據(jù)的邏輯已經(jīng)完成,接下來是寫磁盤緩存。

假如SourceGenerator的onDataReady方法中的磁盤緩存策略不可用,則會(huì)回調(diào)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();
    }
  }

decodeFromRetrievedData();后續(xù)的方法調(diào)用鏈在之前的文章中分析過,主要做的事情就是:將原始的data數(shù)據(jù)轉(zhuǎn)變?yōu)榭梢怨㊣mageView顯示的resource數(shù)據(jù)并將其顯示在ImageView上。

將原始的data數(shù)據(jù)轉(zhuǎn)變?yōu)閞esource數(shù)據(jù)后,會(huì)調(diào)用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時(shí)要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;
  } 

然后是此過程中的磁盤緩存過程,影響的因素有encodeStrategy、DiskCacheStrategy.isResourceCacheable。encodeStrategy根據(jù)resource數(shù)據(jù)的類型來判斷,如果是Bitmap或BitmapDrawable,那么就是TRANSFORMED;如果是GifDrawable,那么就是SOURCE。磁盤緩存策略默認(rèn)是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;
        }
    };
  • 只有dataSource為DataSource.LOCAL且encodeStrategy為EncodeStrategy.TRANSFORMED時(shí),才允許緩存。也就是只有本地的resource數(shù)據(jù)為Bitmap或BitmapDrawable的資源才可以緩存。
  • 在DecodeJob.onResourceDecoded中會(huì)調(diào)用deferredEncodeManager.init(key, encoder, lockedResult);去初始化deferredEncodeManager。

在DecodeJob的decodeFromRetrievedData();中拿到resource數(shù)據(jù)后會(huì)調(diào)用notifyEncodeAndRelease(resource, currentDataSource)利用deferredEncodeManager對(duì)象進(jìn)行磁盤緩存的寫入;

otifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    ...
    // 通知回調(diào),資源已經(jīng)就緒
    notifyComplete(result, dataSource);
    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    onEncodeComplete();
  }

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

diskCacheProvider.getDiskCache()獲取到DiskLruCacheWrapper,并調(diào)用DiskLruCacheWrapper的put寫入。DiskLruCacheWrapper在寫入的時(shí)候會(huì)使用到寫鎖DiskCacheWriteLocker,鎖對(duì)象由對(duì)象池WriteLockPool創(chuàng)建,寫鎖WriteLock實(shí)現(xiàn)是一個(gè)不公平鎖ReentrantLock。

在緩存寫入前,會(huì)判斷key對(duì)應(yīng)的value存不存在,若存在則不寫入。緩存的真正寫入會(huì)由DataCacheWriter交給ByteBufferEncoder和StreamEncoder兩個(gè)具體類來寫入,前者負(fù)責(zé)將ByteBuffer寫入到文件,后者負(fù)責(zé)將InputStream寫入到文件。

目前為止,磁盤緩存的讀寫流程都已分析完成;

5、內(nèi)存緩存:ActiveResource與MemoryCache讀取

回到DecodeJob.notifyEncodeAndRelease方法中,經(jīng)過notifyComplete、EngineJob.onResourceReady、notifyCallbacksOfResult方法中。

在該方法中一方面會(huì)將原始的resource包裝成一個(gè)EngineResource,然后通過回調(diào)傳給Engine.onEngineJobComplete

@Override
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // 設(shè)置資源的回調(diào)為自己,這樣在資源釋放時(shí)會(huì)通知自己的回調(diào)方法
    if (resource != null) {
      resource.setResourceListener(key, this);
      // 將資源放入activeResources中,資源變?yōu)閍ctive狀態(tài)
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }
    // 將engineJob從Jobs中移除
    jobs.removeIfCurrent(key, engineJob);
  }

在這里會(huì)將資源放入activeResources中,資源變?yōu)閍ctive狀態(tài)。后面會(huì)使用Executors.mainThreadExecutor()調(diào)用SingleRequest.onResourceReady回調(diào)進(jìn)行資源的顯示。在觸發(fā)回調(diào)前后各有一個(gè)地方會(huì)對(duì)engineResource進(jìn)行acquire()和release()操作,這兩個(gè)操作分別發(fā)生在notifyCallbacksOfResult()方法的incrementPendingCallbacks、decrementPendingCallbacks()調(diào)用中

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

CallResourceReady的run方法中也會(huì)調(diào)用engineResource.acquire(),上面的代碼調(diào)用結(jié)束后,engineResource的引用計(jì)數(shù)為1。engineResource的引用計(jì)數(shù)會(huì)在RequestManager.onDestory方法中最終調(diào)用SingleRequest.clear()方法,SingleRequest.clear()內(nèi)部調(diào)用releaseResource()、Engine.release 進(jìn)行釋放,這樣引用計(jì)數(shù)就變?yōu)?。引用計(jì)數(shù)就變?yōu)?后會(huì)通知Engine將此資源從active狀態(tài)變成memory cache狀態(tài)。如果我們?cè)俅渭虞d資源時(shí)可以從memory cache中加載,那么資源又會(huì)從memory cache狀態(tài)變成active狀態(tài)。也就是說,在資源第一次顯示后,我們關(guān)閉頁(yè)面,資源會(huì)由active變成memory cache;然后我們?cè)俅芜M(jìn)入頁(yè)面,加載時(shí)會(huì)命中memory cache,從而又變成active狀態(tài)

總結(jié)

  • 讀取內(nèi)存緩存時(shí),先從LruCache算法機(jī)制的內(nèi)存緩存讀取,再?gòu)娜跻脵C(jī)制的內(nèi)存緩存讀?。?/li>
  • 寫入內(nèi)存緩存時(shí),先寫入 弱引用機(jī)制 的內(nèi)存緩存,等到圖片不再被使用時(shí),再寫入到 LruCache算法機(jī)制的內(nèi)存緩存;
  • 讀取磁盤緩存時(shí),先讀取轉(zhuǎn)換后圖片的緩存,再讀取原始圖片的緩存;

B站100集視頻教程【Android源碼解析Retrofit/OkHttp/Glide/RxJava/EventBus....】:https://www.bilibili.com/video/BV1mT4y1R7F4?spm_id_from=333.999.0.0

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容