1. 程式人生 > >Glide4.8原始碼拆解(二)核心載入流程

Glide4.8原始碼拆解(二)核心載入流程

前言

上一篇文章中介紹了Glide基本的呼叫流程,總結起來就是Engine是真正載入資源的入口,SingleRequest起到連線RequestManagerTargetEngine的紐帶關係,本文將承接上文,探討Glide的載入流程。

本章要討論的內容:

  • Engine的工作流程;
  • 記憶體快取ActiveResource原理剖析;
  • 記憶體快取MemoryCache原理剖析;
  • EngineJob快取分析;
  • EngineJob和DecodeJob的工作原理;

在討論Engine之前,還是從呼叫它的地方開始SingleReques.onSizeReady

從Engine開始

SingleRequest.java

@Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    if (IS_VERBOSE_LOGGABLE) {
      logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;

    float
sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = maybeApplySizeMultiplier(width, sizeMultiplier); this.height = maybeApplySizeMultiplier(height, sizeMultiplier); if (IS_VERBOSE_LOGGABLE) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } //建立Engine物件 loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this);//最後一個this是回撥介面 // have completed asynchronously. if
(status != Status.RUNNING) { loadStatus = null; } if (IS_VERBOSE_LOGGABLE) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } } 複製程式碼

Engine.load()方法有很多引數,其中大部分是從requestOptions中獲得,值得注意的是最後一個引數ResourceCallback,,由於Engine的快取載入邏輯是非同步的,所以SingleRequest得到Engine的結果就全在實現方法onResourceReady()onLoadFailed()裡了;SingleRequest的回撥不再講解,我們要往底層探索,從Engine這個類開始;注意,Engine.load()的呼叫還在主執行緒中;

Engine的初始化和載入流程

Engine.java

public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {
    
    private final Jobs jobs;
    private final MemoryCache cache;
    private final EngineJobFactory engineJobFactory;
    private final ResourceRecycler resourceRecycler;
    private final LazyDiskCacheProvider diskCacheProvider;
    private final DecodeJobFactory decodeJobFactory;
    private final ActiveResources activeResources;
  
    //構造方法
    Engine(MemoryCache cache,
      DiskCache.Factory diskCacheFactory,
      GlideExecutor diskCacheExecutor,
      GlideExecutor sourceExecutor,
      GlideExecutor sourceUnlimitedExecutor,
      GlideExecutor animationExecutor,
      Jobs jobs,
      EngineKeyFactory keyFactory,
      ActiveResources activeResources,
      EngineJobFactory engineJobFactory,
      DecodeJobFactory decodeJobFactory,
      ResourceRecycler resourceRecycler,
      boolean isActiveResourceRetentionAllowed) {
    this.cache = cache;
    //建立diskCacheProvider
    this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);
    //建立activeResources
    if (activeResources == null) {
      activeResources = new ActiveResources(isActiveResourceRetentionAllowed);
    }
    this.activeResources = activeResources;
    //監聽
    activeResources.setListener(this);
    //建立EngineKeyFactory()
    if (keyFactory == null) {
      keyFactory = new EngineKeyFactory();
    }
    this.keyFactory = keyFactory;
    //建立Jobs
    if (jobs == null) {
      jobs = new Jobs();
    }
    this.jobs = jobs;
    //建立engineJobFactory
    if (engineJobFactory == null) {
      engineJobFactory =
          new EngineJobFactory(
              diskCacheExecutor, sourceExecutor, sourceUnlimitedExecutor, animationExecutor, this);
    }
    this.engineJobFactory = engineJobFactory;
    //建立decodeJobFactory
    if (decodeJobFactory == null) {
      decodeJobFactory = new DecodeJobFactory(diskCacheProvider);
    }
    this.decodeJobFactory = decodeJobFactory;
    //建立resourceRecycler
    if (resourceRecycler == null) {
      resourceRecycler = new ResourceRecycler();
    }
    this.resourceRecycler = resourceRecycler;
    //監聽
    cache.setResourceRemovedListener(this);
  }
  
    public <R> LoadStatus load(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      Class<?> resourceClass,
      Class<R> transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map<Class<?>, Transformation<?>> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      Options options,
      boolean isMemoryCacheable,
      boolean useUnlimitedSourceExecutorPool,
      boolean useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb) {
    Util.assertMainThread();
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

    //獲得key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
    //從當前正在使用的Resources裡面去
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
        //如果命中,直接回調結果
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      //返回null
      return null;
    }
    //從記憶體快取中獲取
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
     //如果命中,直接回調結果
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
       //返回null
      return null;
    }
    //以上都沒有命中,試圖從已存在的任務中對應的EngineJob
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
        //如果去到,把cb往下傳遞
      current.addCallback(cb);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      //返回結果
      return new LoadStatus(cb, current);
    }
    //取不到建立下新的EngineJob
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
    //建立新的DecodeJob
    DecodeJob<R> decodeJob =
        decodeJobFactory.build(
            glideContext,
            model,
            key,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            onlyRetrieveFromCache,
            options,
            engineJob);
    //將當前的engineJob新增到快取中
    jobs.put(key, engineJob);
    //回撥往下傳遞
    engineJob.addCallback(cb);
    //engineJob開始執行
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }
}
複製程式碼

在Engine的構造方法中,建立了預設的各種factory和容器,諸如engineJobFactorydecodeJobFactoryJobsactiveResources等,各種引數先不一一介紹,我們看load()方法,這是整個呼叫的出發點;我在程式碼中已經註釋的很清晰,下面再梳理一遍流程:

load流程

  1. 通過keyFactory和請求引數,建立EngineKey物件key;
  2. 呼叫loadFromActiveResources()方法,嘗試從活動的Resources中獲取active;
  3. 如果步驟2命中,直接回調cb.onResourceReady(),並返回,不命中,執行步驟4;
  4. 呼叫loadFromCache()方法,嘗試從記憶體快取中獲取cached;
  5. 如果步驟4命中,直接回調cb.onResourceReday(),並返回,不命中,執行步驟6;
  6. 嘗試從jobs中獲取匹配key的正在執行的EngineJob current;
  7. 如果步驟6命中,把回撥新增到current並返回,不命中,執行步驟8;
  8. 通過engineJobFactory建立新的EngineJob物件engineJob;
  9. 根據decodeJobFactory建立新的DecodeJob物件decodeJob;
  10. 把engineJob新增進jobs中,講回撥cb設定到engineJob中;
  11. 執行engineJob.start(decodeJob);

接下來我們從loadFromActiveResources開始,分析感興趣的方法

loadFromActiveResources流程

Engine.java

private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
      return null;
    }
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }
    return active;
  }
複製程式碼

ActiveResource的要從activeResources中獲取,activeResources在Engine構造方法中建立,我們分析ActiveResource類的簡單實現;

ActiveResource.java

final class ActiveResources {

    private static final int MSG_CLEAN_REF = 1;
    
    final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
    private ReferenceQueue<EngineResource<?>> resourceReferenceQueue;
    private Thread cleanReferenceQueueThread;
    
    private ResourceListener listener;//一般engine監聽次方法
    //快取的複用在主執行緒中執行
    private final Handler mainHandler = new Handler(Looper.getMainLooper(), new Callback() {
    @Override
    public boolean handleMessage(Message msg) {
      if (msg.what == MSG_CLEAN_REF) {
        cleanupActiveReference((ResourceWeakReference) msg.obj);
        return true;
      }
      return false;
    }
  });
  
    //設定監聽
    void setListener(ResourceListener listener) {
    this.listener = listener;
    }
    
    //get方法
    EngineResource<?> get(Key key) {
    ResourceWeakReference activeRef = activeEngineResources.get(key);
    if (activeRef == null) {
      return null;
    }
    EngineResource<?> active = activeRef.get();
    if (active == null) {
      cleanupActiveReference(activeRef);
    }
    return active;
  }
  
  //activate方法,相當於put
  void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key,
            resource,
            getReferenceQueue(),
            isActiveResourceRetentionAllowed);

    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();//移除的弱引用物件需要清除強引用
    }
  }
  
  //清除當前被GC的ref物件
  void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
    Util.assertMainThread();
    activeEngineResources.remove(ref.key);//從集合中移除掉

    if (!ref.isCacheable || ref.resource == null) {
      return;
    }
    //建立新的物件EngineResource,複用ref.resource,
    EngineResource<?> newResource =
        new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
    newResource.setResourceListener(ref.key, listener);
    listener.onResourceReleased(ref.key, newResource);
  }
  
    //建立resourceReferenceQueue,用來監聽垃圾回收物件
    if (resourceReferenceQueue == null) {
      resourceReferenceQueue = new ReferenceQueue<>();
      //建立執行緒監聽弱引用回收對列
      cleanReferenceQueueThread = new Thread(new Runnable() {
        @SuppressWarnings("InfiniteLoopStatement")
        @Override
        public void run() {
          //設定執行緒優先順序有後臺執行緒
          Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
          cleanReferenceQueue();
        }
      }, "glide-active-resources");
      cleanReferenceQueueThread.start();
    }
    return resourceReferenceQueue;
  }
  //shutdown中斷執行緒,清除佇列
  void shutdown() {
    isShutdown = true;
    if (cleanReferenceQueueThread == null) {
      return;
    }

    cleanReferenceQueueThread.interrupt();
    try {
      cleanReferenceQueueThread.join(TimeUnit.SECONDS.toMillis(5));
      if (cleanReferenceQueueThread.isAlive()) {
        throw new RuntimeException("Failed to join in time");
      }
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
    }
  }

  //清除回收物件
@Synthetic void cleanReferenceQueue() {
    while (!isShutdown) {
      try {
        ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
        mainHandler.obtainMessage(MSG_CLEAN_REF, ref).sendToTarget();

        // This section for testing only.
        DequeuedResourceCallback current = cb;
        if (current != null) {
          current.onResourceDequeued();
        }
        // End for testing only.
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    }
  }
  //弱引用監聽物件,強引用儲存真正的資源
  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);
      //儲存key
      this.key = Preconditions.checkNotNull(key);
      //儲存resource,強引用
      this.resource =
          referent.isCacheable() && isActiveResourceRetentionAllowed
              ? Preconditions.checkNotNull(referent.getResource()) : null;
      isCacheable = referent.isCacheable();
    }

    //清除強引用
    void reset() {
      resource = null;
      clear();
    }
  }
    
}
複製程式碼

ActiveResource快取原理

ActiveResources採用HashMap+WeakRefence方式儲存EngineResource物件,沒有對集合size做限制,在使用WeakReference的時候,建立了一個ReferenceQueue,來記錄被GC回收的EngineResource物件,而且在建立ReferenceQueue時生成了一個後臺執行緒cleanReferenceQueueThread,不斷地執行cleanReferenceQueue()方法,一旦ReferenceQueue取出不為空,便取出ref物件,執行cleanupActiveReference()方法

有必要看一下EngineResource類結構:

EngineResource.java

class EngineResource<Z> implements Resource<Z> {
  private final boolean isCacheable;
  private final boolean isRecyclable;
  private ResourceListener listener;
  private Key key;
  private int acquired;
  private boolean isRecycled;
  private final Resource<Z> resource;//真正的resource

  interface ResourceListener {
    void onResourceReleased(Key key, EngineResource<?> resource);
  }

  EngineResource(Resource<Z> toWrap, boolean isCacheable, boolean isRecyclable) {
    resource = Preconditions.checkNotNull(toWrap);
    this.isCacheable = isCacheable;
    this.isRecyclable = isRecyclable;
  }

  void setResourceListener(Key key, ResourceListener listener) {
    this.key = key;
    this.listener = listener;
  }

  Resource<Z> getResource() {
    return resource;
  }
}
複製程式碼

本質上EngineResource是對Resource的包裝類,所以下面的gc分析一定要區分EngineResourceResource,這倆不是一個物件;

牛掰的弱引用複用機制

ResourceWeakReference這個類不簡單,它本意是對EngineResource的弱引用,其實在構造它時候,會把EngineResource.resource和EngineResource.key以強引用的形式儲存,所以垃圾回收的是EngineResource,卻回收不掉EngineResource.resource,因為此時resource會被ResourceWeakReference引用;

cleanupActiveReference()首先取出ref.resource,這個物件是強引用,不會被回收,被回收的是ref包裝的EngineResource;然後建立新的EngineResource包裝真正的resource,最終呼叫資源回收的監聽listener.onResourceReleased(ref.key, newResource),而setListener()Engine構造方法中呼叫;看一下Engine.onResourceReleased()方法的實現:

Engine.java

public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    Util.assertMainThread();
    //把key對應的ResourceWeakReference從Map中移除
    activeResources.deactivate(cacheKey);
    if (resource.isCacheable()) {
        //記憶體快取複用
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }
複製程式碼

Engine在onResourceReleased時,重新儲存了EngineResource物件,並且在此之前,還呼叫了activeResources.deactivate(cacheKey);

為什麼要deactivate,下面解釋一下原因:

因為在ActiveResources.cleanupActiveReference()中建立新的EngineResource來包裝被回收的EngineResource下面的resource,但是這個resource還在被ref強引用,所以執行activeResources.deactivate(cacheKey)會清除ref多resource的強引用;

弄明白了這些,ActiveResources原理基本上搞明白了;

小結:

ActiveResources採用弱引用的方式,記錄EngineResource的回收情況,同時採取強引用儲存EngineResource.resource,在ActiveResources中會有個後臺執行緒會執行清理工作,一旦發現某個EngineResource被回收,就會拿出其對應的resource,然後建立一個新的EngineResource包裝這個resource,之後回撥給Engine,讓其做記憶體快取,最後Engine呼叫activeResources.deactivate(cacheKey)解除refresource強引用。

loadFromCache()流程分析

未完待續睡覺...