Glide4.5原始碼分析一

weixin_33938733發表於2018-01-23

這篇摸清一個問題:Glide從網上下載圖片的流程.
Glide4.0加入了Generated API,如此現在有兩種基本載入方式:
Glide.with(context).load(url) .into(imageView);
GlideApp.with(context).load(url) .into(imageView);
而GlideApp.with(context) 其實也是呼叫的Glide.with(context)方法,
Glide.with(context)的目的就是為了獲取一個RequestManager,下面看看獲取RequestManager的過程:
Glide.java中

  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }
@NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }
 /**
   * Get the singleton.
   *
   * @return the singleton
   */
  @NonNull
  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);//建立glide例項開始
        }
      }
    }

    return glide;
  }

glide例項真正建立的方法:

@SuppressWarnings("deprecation")
  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
  //省略一些程式碼
    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    builder.setRequestManagerFactory(factory);
   
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    Glide glide = builder.build(applicationContext);//關鍵點,GlideBuilder建立glide例項
   
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

再來看GlideBuilder build glide過程:

@NonNull
  public Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();//網路載入執行緒池
    }

    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();//磁碟快取執行緒池
    }

    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();  
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    if (bitmapPool == null) {
     //在memorySizeCalculator中size被計算,如果當前裝置isLowMemoryDevice並且
     //Build.VERSION.SDK_INT >= Build.VERSION_CODES.O,則不會使用圖片快取
     // On Android O+ Bitmaps are allocated natively, ART is much more efficient at managing
      // garbage and we rely heavily on HARDWARE Bitmaps, making Bitmap re-use much less important.
      // We prefer to preserve RAM on these devices and take the small performance hit of not
      // re-using Bitmaps and textures when loading very small images or generating thumbnails.
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();//get永遠返回null,只是為了適配BitmapPool的設計,利於維護
      }
    }

    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
    //建立engine物件,它將是圖片請求的發起者
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              GlideExecutor.newAnimationExecutor(),
              isActiveResourceRetentionAllowed);
    }

    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory);

    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptions.lock(),
        defaultTransitionOptions);
  }

關心主流程,到這裡,glide物件被建立,Glide.with()是獲取RequestManager的過程,開始獲取在下面方法中:

@NonNull
  public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }

    return getApplicationManager(context);
  }

通過 get((FragmentActivity) context);和get((Activity) context);真正獲取RequestManager在supportFragmentGet(),fragmentGet()兩個方法,兩個方法功能一致,分析其中一個:

@NonNull
  private RequestManager supportFragmentGet(@NonNull Context context, @NonNull FragmentManager fm,
      @Nullable Fragment parentHint) {
    //建立一個隱藏fragment用於管理Glide載入生命週期
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
        //建立RequestManager
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);//
      //將requestManager儲存在建立的fragment中,用於生命週期管理,先不分析.
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

這樣我們就獲取到了requestManager例項,接下來看.load(url)這一節,load()方法有很多過載方法,我們找最簡單的一種load(@Nullable String string):
RequestManager.java中

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

跟蹤asDrawable()方法進入下面方法:

@NonNull
 @CheckResult
 public <ResourceType> RequestBuilder<ResourceType> as(
     @NonNull Class<ResourceType> resourceClass) {
//ResourceType型別為Drawable
   return new RequestBuilder<>(glide, this, resourceClass, context);//建立一個RequestBuilder例項.
 }

於是asDrawable().load(string);變成呼叫RequestBuilder中load(string):

* @param string A file path, or a uri or url handled by
   * {@link com.bumptech.glide.load.model.UriLoader}.
   */
  @NonNull
  @Override
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
@NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;//現在這裡是String型別
    isModelSet = true;
    return this;
  }

到這裡load()結束,進入into(),真正載入開始

 @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    RequestOptions requestOptions = this.requestOptions;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // Clone in this method so that if we use this RequestBuilder to load into a View and then
      // into a different target, we don't retain the transformation applied based on the previous
      // View's scale type.
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }
    //重點,glideContext是在Glide構造方法中建立的,GlideContext類是一個全域性的為所有在Glide中
    //載入任務儲存和提供各種registries和classes的類,就是一個類似工具類的存在
    //transcodeClass:這裡我們傳入的是Drawable (在RequestManager中的as()方法傳入)
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

跟蹤glideContext.buildImageViewTarget()進入ImageViewTargetFactory中,一個生產為不同View生產正確ViewTarget的工廠:

public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
      @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);//我們是
      //Drawable所以返回的是DrawableImageViewTarget
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }

ViewTarget建立後我們來進入上面into方法:

 private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    options = options.autoClone();
    Request request = buildRequest(target, targetListener, options);//重點,建立載入圖片請求

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }

接著跟蹤buildRequest(target, targetListener, options)方法進入:

private Request buildRequestRecursive(
      Target<TranscodeType> target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      RequestOptions requestOptions) {

    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
    ErrorRequestCoordinator errorRequestCoordinator = null;
    //這裡如果呼叫了RequestBuilder error()將會進行錯誤處理
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
    //重點
    Request mainRequest =
        buildThumbnailRequestRecursive(
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions);

    if (errorRequestCoordinator == null) {
      return mainRequest;
    }

    int errorOverrideWidth = errorBuilder.requestOptions.getOverrideWidth();
    int errorOverrideHeight = errorBuilder.requestOptions.getOverrideHeight();
    if (Util.isValidDimensions(overrideWidth, overrideHeight)
        && !errorBuilder.requestOptions.isValidOverride()) {
      errorOverrideWidth = requestOptions.getOverrideWidth();
      errorOverrideHeight = requestOptions.getOverrideHeight();
    }

    Request errorRequest = errorBuilder.buildRequestRecursive(
        target,
        targetListener,
        errorRequestCoordinator,
        errorBuilder.transitionOptions,
        errorBuilder.requestOptions.getPriority(),
        errorOverrideWidth,
        errorOverrideHeight,
        errorBuilder.requestOptions);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }

繼續跟蹤:

private Request buildThumbnailRequestRecursive(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      @Nullable RequestCoordinator parentCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      RequestOptions requestOptions) {
      .......
     //省略縮圖的出來程式碼
      .........
      // Base case: no thumbnail.重點
      return obtainRequest(
          target,
          targetListener,
          requestOptions,
          parentCoordinator,
          transitionOptions,
          priority,
          overrideWidth,
          overrideHeight);
    }
  }

隨後我們進入SingleRequest.obtain()方法這個方法真正生成一個request:SingleRequest例項,通過打斷點發現請求都會進入SingleRequest的begin()方法,什麼時候呼叫這個方法跟生命週期有關,在onStart()方法執行後,也有不是生命週期方法呼叫的.比如View在AttachedToWindow後,後面分析具體呼叫時機.

@Override
  public void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      // Only log at more verbose log levels if the user has set a fallback drawable, because
      // fallback Drawables indicate the user expects null models occasionally.
      int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }

    if (status == Status.RUNNING) {
      throw new IllegalArgumentException("Cannot restart a running request");
    }

    // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
    // that starts an identical request into the same Target or View), we can simply use the
    // resource and size we retrieved the last time around and skip obtaining a new size, starting a
    // new load etc. This does mean that users who want to restart a load because they expect that
    // the view size has changed will need to explicitly clear the View or Target before starting
    // the new load.
    if (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }

    // Restarts for requests that are neither complete nor running can be treated as new requests
    // and can run again from the beginning.

    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);//重點
    } else {
      target.getSize(this);
    }

    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }

略過其他方法,看重點方法onSizeReady(int width, int height)

 /**
   * A callback method that should never be invoked directly.
   * 注意這句註釋,callback方法,不會被直接呼叫,說明只有當target的size確定後才會呼叫
   * 這個方法,才會真正的通過engine載入圖片
   */
  @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));
    }
    // 重點 
    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 is a hack that's only useful for testing right now where loads complete synchronously
    // even though under any executor running on any thread but the main thread, the load would
    // have completed asynchronously.
    if (status != Status.RUNNING) {
      loadStatus = null;
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
  }

繼續分析重點engine.load()

/**
  * Starts a load for the given arguments.
  *
  * <p>Must be called on the main thread.
  *
  * <p>The flow for any request is as follows:
  * <ul>
  *   <li>Check the current set of actively used resources, return the active resource if
  *   present, and move any newly inactive resources into the memory cache.</li>
  *   <li>Check the memory cache and provide the cached resource if present.</li>
  *   <li>Check the current set of in progress loads and add the cb to the in progress load if
  *   one is present.</li>
  *   <li>Start a new load.</li>
  * </ul>
  *
  * <p>Active resources are those that have been provided to at least one request and have not yet
  * been released. Once all consumers of a resource have released that resource, the resource then
  * goes to cache. If the resource is ever returned to a new consumer from cache, it is re-added to
  * the active resources. If the resource is evicted from the cache, its resources are recycled and
  * re-used if possible and the resource is discarded. There is no strict requirement that
  * consumers release their resources so active resources are held weakly.
  *
  * @param width  The target width in pixels of the desired resource.
  * @param height The target height in pixels of the desired resource.
  * @param cb     The callback that will be called when the load completes.
  */
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 = LogTime.getLogTime();
   //這個key是用來標記每一個請求的
   EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
       resourceClass, transcodeClass, options);
   // 請求資源第一步 從active resources 中拿取
   EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
   if (active != null) {
     cb.onResourceReady(active, DataSource.MEMORY_CACHE);
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
       logWithTimeAndKey("Loaded resource from active resources", startTime, key);
     }
     return null;
   }
   //請求資源第二步 從MemoryCache 中拿取
   EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
   if (cached != null) {
     cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
       logWithTimeAndKey("Loaded resource from cache", startTime, key);
     }
     return null;
   }
  
   EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
   if (current != null) {
     current.addCallback(cb);
     if (Log.isLoggable(TAG, Log.VERBOSE)) {
       logWithTimeAndKey("Added to existing load", startTime, key);
     }
     return new LoadStatus(cb, current);
   }
   
// active resources 和 MemoryCache中都沒有我們要請求的資源,建立一個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);

   jobs.put(key, engineJob);

   engineJob.addCallback(cb);//重點 新增圖片資源下載完成後的回撥
   engineJob.start(decodeJob);//重點 開始下載任務

   if (Log.isLoggable(TAG, Log.VERBOSE)) {
     logWithTimeAndKey("Started new load", startTime, key);
   }
   return new LoadStatus(cb, engineJob);
 }

繼續分析engine.start()方法:

public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();//這裡決定用什麼執行緒池來執行,從磁碟快取中獲取使用
        //diskCacheExecutor,我們拿到的是diskCacheExecutor
    executor.execute(decodeJob);
  }
/**
   * Returns true if this job will attempt to decode a resource from the disk cache, and false if it
   * will always decode from source.
   */
  boolean willDecodeFromCache() {
    Stage firstStage = getNextStage(Stage.INITIALIZE);
    return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;//返回true
  }

DecodeJob是一個Runnable,接下來看他的run方法:

 @Override
  public void run() {
    // This should be much more fine grained, but since Java's thread pool implementation silently
    // swallows all otherwise fatal exceptions, this will at least make it obvious to developers
    // that something is failing.
    TraceCompat.beginSection("DecodeJob#run");
    // Methods in the try statement can invalidate currentFetcher, so set a local variable here to
    // ensure that the fetcher is cleaned up either way.
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();//重點
    } catch (Throwable t) {
      // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our
      // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We
      // are however ensuring that our callbacks are always notified when a load fails. Without this
      // notification, uncaught throwables never notify the corresponding callbacks, which can cause
      // loads to silently hang forever, a case that's especially bad for users using Futures on
      // background threads.
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "DecodeJob threw unexpectedly"
            + ", isCancelled: " + isCancelled
            + ", stage: " + stage, t);
      }
      // When we're encoding we've already notified our callback and it isn't safe to do so again.
      if (stage != Stage.ENCODE) {
        throwables.add(t);
        notifyFailed();
      }
      if (!isCancelled) {
        throw t;
      }
    } finally {
      // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
      // close in all cases anyway.
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      TraceCompat.endSection();
    }
  }
//Stage 是一個狀態標記,標記從哪裡decode資料
//原始碼註釋:Where we're trying to decode data from.
private void runWrapped() {
     switch (runReason) {
      case INITIALIZE://請求開始是這個狀態,所以第一次執行這裡
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();//第一次拿到ResourceCacheGenerator
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }
//獲取下一次狀態
private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE://第一次執行這裡,預設的disk快取策略是AUTOMATIC,所以狀態切換成RESOURCE_CACHE
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
      //第二次執行到這裡,預設AUTOMATIC策略decodeCachedData()返回true
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }
private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);//ResourceCache,載入本地Uri資源圖片後的快取
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);//DataCache我的理解是網路圖片disk快取
      case SOURCE:
        return new SourceGenerator(decodeHelper, this);//網路載入器
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }
private void runGenerators() {
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {//關鍵點
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) {
        reschedule();//關鍵點
        return;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }

currentGenerator.startNext(),DataFetcherGenerator 有3個實現,分別對應ResourceCacheGenerator,DataCacheGenerator,SourceGenerator.總結下圖片在進入disk快取獲取的步驟,也就是DecodeJob中run()方法執行的工作:

  1. runWrapped()方法中,runReason第一次是INITIALIZE,獲取到的stage是Stage.RESOURCE_CACHE,currentGenerator是ResourceCacheGenerator,隨後執行runGenerators()方法.


    1334897-971090ad7b341877.png
    runWrapped()第一次執行.png
1334897-5b72b8be3d2de3f3.png
Generator切換過程.png
  1. reschedule方法中再次執行run()方法
 @Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
  }

4.最後再次執行runGenerators()方法,進入SourceGenerator的startNext()方法.開始網路載入

SourceGenerator的startNext()方法:

@Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    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);//重點開始載入,並且傳入回撥this
      }
    }
    return started;
  }

我引入了Okhttp的網路載入,所以這裡的fetcher是OkHttpStreamFetcher,看他的loadData()實現:

@Override
  public void loadData(@NonNull Priority priority,
      @NonNull final DataCallback<? super InputStream> callback) {
    Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
    for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
      String key = headerEntry.getKey();
      requestBuilder.addHeader(key, headerEntry.getValue());
    }
    Request request = requestBuilder.build();
    this.callback = callback;

    call = client.newCall(request);
    call.enqueue(this);
  }

圖片請求回撥

 @Override
  public void onFailure(@NonNull Call call, @NonNull IOException e) {
    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "OkHttp failed to obtain result", e);
    }

    callback.onLoadFailed(e);
  }

  @Override
  public void onResponse(@NonNull Call call, @NonNull Response response) {
    responseBody = response.body();
    if (response.isSuccessful()) {
      long contentLength = Preconditions.checkNotNull(responseBody).contentLength();
      stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
      callback.onDataReady(stream);
    } else {
      callback.onLoadFailed(new HttpException(response.message(), response.code()));
    }
  }

這裡的callback就是前面傳入的SourceGenerator,看他的onDataReady方法:

@Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();//重點這個cb callback是DecodeJob
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }

DecodeJob的reschedule():

@Override
  public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);//callback是EngineJob
  }

EngineJob的reschedule()

@Override
  public void reschedule(DecodeJob<?> job) {
    // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
    // up.
    getActiveSourceExecutor().execute(job);//可以看到這裡的執行緒池已經切到網路執行緒池
  }

隨後DecodeJob的run被執行,跟蹤方法再次進入SourceGenerator的startNext()方法

@Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);//重點,前面已經獲取到資料dataToCache不再為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;
  }

快取資料

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);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished encoding source to cache"
            + ", key: " + originalKey
            + ", data: " + dataToCache
            + ", encoder: " + encoder
            + ", duration: " + LogTime.getElapsedMillis(startTime));
      }
    } finally {
      loadData.fetcher.cleanup();
    }
  //sourceCacheGenerator不再為null
    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }

所以在上面的方法中會去執行DataCacheGenerator的startNex()方法,和SourceGenerator類似,隨後執行DataCacheGenerator的對應回撥方法:

@Override
  public void onDataReady(Object data) {
//cb是SourceGenerator
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
  }

  @Override
  public void onLoadFailed(@NonNull Exception e) {
    cb.onDataFetcherFailed(sourceKey, e, loadData.fetcher, DataSource.DATA_DISK_CACHE);
  }

SourceGenerator

// Called from source cache generator.
  @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    // This data fetcher will be loading from a File and provide the wrong data source, so override
    // with the data source of the original fetcher
  //cb 是DecodeJob
    cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
  }

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 {
      TraceCompat.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();//重點
      } finally {
        TraceCompat.endSection();
      }
    }
  }

decodeFromRetrievedData()中將會去decode我們需要的資料

private void decodeFromRetrievedData() {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Retrieved data", startFetchTime,
          "data: " + currentData
          + ", cache key: " + currentSourceKey
          + ", fetcher: " + currentFetcher);
    }
    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();
    }
  }
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }

    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }

    notifyComplete(result, dataSource);//重點,這裡資料已經載入出來

    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    // Call onEncodeComplete outside the finally block so that it's not called if the encode process
    // throws.
    onEncodeComplete();
  }
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
  //callback 是EngineJob
    callback.onResourceReady(resource, dataSource);
  }

EngineJob中

@Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    this.resource = resource;
    this.dataSource = dataSource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
  //重點,這裡通過主執行緒handler將當前EngineJob傳送到主執行緒訊息佇列中
  }

主執行緒handler處理訊息:

@Override
    public boolean handleMessage(Message message) {
      EngineJob<?> job = (EngineJob<?>) message.obj;
      switch (message.what) {
        case MSG_COMPLETE:
          job.handleResultOnMainThread();
          break;
        case MSG_EXCEPTION:
          job.handleExceptionOnMainThread();
          break;
        case MSG_CANCELLED:
          job.handleCancelledOnMainThread();
          break;
        default:
          throw new IllegalStateException("Unrecognized message: " + message.what);
      }
      return true;
    }

handleResultOnMainThread()

@Synthetic
  void handleResultOnMainThread() {
    stateVerifier.throwIfRecycled();
    if (isCancelled) {
      resource.recycle();
      release(false /*isRemovedFromQueue*/);
      return;
    } else if (cbs.isEmpty()) {
      throw new IllegalStateException("Received a resource without any callbacks to notify");
    } else if (hasResource) {
      throw new IllegalStateException("Already have resource");
    }
    engineResource = engineResourceFactory.build(resource, isCacheable);
    hasResource = true;

    // Hold on to resource for duration of request so we don't recycle it in the middle of
    // notifying if it synchronously released by one of the callbacks.
    engineResource.acquire();
    listener.onEngineJobComplete(this, key, engineResource);

    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = cbs.size(); i < size; i++) {
      ResourceCallback cb = cbs.get(i);
      if (!isInIgnoredCallbacks(cb)) {
        engineResource.acquire();
        cb.onResourceReady(engineResource, dataSource);//重點
      }
    }
    // Our request is complete, so we can release the resource.
    engineResource.release();

    release(false /*isRemovedFromQueue*/);
  }

我們只關心主要方法 cb.onResourceReady(engineResource, dataSource)
cb的實現是最初的SingleRequest:

private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    if (glideContext.getLogLevel() <= Log.DEBUG) {
      Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
          + dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
          + LogTime.getElapsedMillis(startTime) + " ms");
    }

    isCallingCallbacks = true;
    try {
      if ((requestListener == null
          || !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
          && (targetListener == null
          || !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);//重點
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
  }

這裡的target的實現是ImageViewTarget,ImageViewTarget也是一個抽象類

 public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);//重點
    } else {
      maybeUpdateAnimatable(resource);
    }
  }
 private void setResourceInternal(@Nullable Z resource) {
    // Order matters here. Set the resource first to make sure that the Drawable has a valid and
    // non-null Callback before starting it.
    setResource(resource);//重點這裡是一個抽象方法,我們這裡的實現是DrawableImageViewTarget
    maybeUpdateAnimatable(resource);
  }

DrawableImageViewTarget

@Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);//到這裡view真正獲取到圖片Drawable
  }

總結一下圖片載入的主流程:

  1. Glide.with(context) 會去獲取一個RequestManager,這期間,如果Glide例項沒有,會建立一個全域性單例項.在建立RequestManager時,會去將RequestManager繫結到一個隱藏的RequestManagerFragment中進行生命週期管理.
  2. load()將確定具體的載入型別儲存到一個RequestBuilder中,它也儲存著其他RequestOptions
  3. into()將在RequestBuilder中建立一個SingRequest,隨後onSizeReady()方法被回撥,engine開始發起圖片載入.
  4. Engine圖片載入分為3步,第一步從active resources 中取,active resources 是那些至少已經被提供給一個request的並且還沒有被釋放的,第二步從MemoryCache中獲取,如果還是沒有,第三步將會建立一個新的請求(ps:這個方法註釋非常清晰,Active resources are those that have been provided to at least one request and have not yet been released. Once all consumers of a resource have released that resource, the resource then goes to cache. If the resource is ever returned to a new consumer from cache, it is re-added to the active resources. If the resource is evicted from the cache, its resources are recycled and re-used if possible and the resource is discarded. There is no strict requirement that consumers release their resources so active resources are held weakly.)
  5. 在Engine的load()中建立一個DecodeJob,Engine將它放入執行緒池中執行,decodeJob先從ResourceCache中獲取,再從DataCache中,最後SourceGenerator的startNext()方法中發起網路請求.網路載入完成後先快取下來,DecodeJob再次執行,在DataCacheGenerator的startNext()中獲取到資料,經過層層回撥在DecodeJob的decodeFromRetrievedData()方法中將resource解析出來,最後回撥到EngineJob的onResourceReady()方法,通過主執行緒Handler完成執行緒切換並將當前EngineJob通過Message傳送到主執行緒的訊息佇列,隨後處理,接著回撥到SingRequest的onResourceReady()方法,隨後回撥target的onResourceReady()方法,到這裡圖片載入到對應的target,完成.

相關文章