Glide 4.9 源碼分析(一) —— 一次完整加載流程

前言

若想把握 Glide 圖片加載的精髓, 首先要理清 Glide 圖片加載的一次流程

// 這里便是與 Glide 3+ 的不同
RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.loading);
// 它需要一個(gè)
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);

好的, 可以看到 Glide 的使用方式極為簡單, 但往往越是簡單的背后, 越是隱藏了復(fù)雜的實(shí)現(xiàn), 接下來我們就一步一步的分析 Glide 4.9 的一次加載流程

一. Glide.with

public class Glide implements ComponentCallbacks2 {

  public static RequestManager with(@NonNull Context context) {
    // 1\. 調(diào)用了 getRetriever 獲取一個(gè) RequestManagerRetriever
    // 2\. 調(diào)用了 RequestManagerRetriever.get 獲取一個(gè) RequestManager 描述一個(gè)圖片加載請(qǐng)求的管理者
    return getRetriever(context).get(context);
  }

  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // 1.1 調(diào)用了 Glide.get 獲取 Glide 對(duì)象
    // 1.2 通過 Glide 對(duì)象獲取一個(gè) RequestManagerRetriever
    // 這個(gè) Retriever 用來獲取一個(gè) RequestManager 對(duì)象, 可以參考 Android Framework 源碼中的 SystemRetriever
    return Glide.get(context).getRequestManagerRetriever();
  }
}

好的, 可以看到 Glide.with 操作, 主要做了兩件事情

  • 通過 Glide.getRetriever 獲取一個(gè) RequestManagerRetriever 對(duì)象, 它描述為請(qǐng)求管理的獲取器
    • 調(diào)用 Glide.get 獲取 Glide 對(duì)象
    • 調(diào)用 Glide.getRequestManagerRetriever, 獲取 RequestManagerRetriever 對(duì)象
  • 調(diào)用 getRequestManagerRetriever.get 獲取一個(gè) RequestManager

接下來我們一步一步的看, 首先是獲取 RequestManagerRetriever

一) 獲取 RequestManagerRetriever

從上面的分析可只, RequestManagerRetriever 是通過 Glide.getRequestManagerRetriever 獲取到的, 因此需要先獲取 Glide 對(duì)象的實(shí)例, 因此我們先看看這個(gè) Glide 是如何構(gòu)造的

public class Glide implements ComponentCallbacks2 {

  private static volatile Glide glide;

  public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }
    return glide;
  }

  private static volatile boolean isInitializing;

  private static void checkAndInitializeGlide(@NonNull Context context) {
    if (isInitializing) {
      // 拋出二次初始的異常
    }
    isInitializing = true;
    // 進(jìn)行初始化操作
    initializeGlide(context);
    isInitializing = false;
  }

  private static void initializeGlide(@NonNull Context context) {
    // 創(chuàng)建了一個(gè) GlideBuilder() 實(shí)例傳入 initializeGlide, 很顯然是為了初始化 Glide 對(duì)象
    // 接下來我們就看看它是如何初始化 Glide 的
    initializeGlide(context, new GlideBuilder());
  }

  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    // 1\. 獲取 @GlideModule 注解驅(qū)動(dòng)生成的 GeneratedAppGlideModuleImpl 和 GeneratedAppGlideModuleFactory 類
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    ......
    // 2\. 嘗試從注解生成的 annotationGeneratedModule 中獲取 RequestManager 的構(gòu)造工廠對(duì)象
    RequestManagerRetriever.RequestManagerFactory factory = annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    // 3\. 向 Glide 的 Builder 中添加這個(gè)請(qǐng)求管理器的構(gòu)造工廠
    builder.setRequestManagerFactory(factory);
    ......
    // 4\. 構(gòu)建 Glide 的實(shí)體對(duì)象
    Glide glide = builder.build(applicationContext);
    ......
    // 5\. 向 Application 中注冊(cè)一個(gè)組件的回調(diào), 用于檢測系統(tǒng) Config 改變和內(nèi)存占用量低的信號(hào)
    applicationContext.registerComponentCallbacks(glide);
    // 保存在靜態(tài)的成員變量中
    Glide.glide = glide;
  }
}
""

好的, 可以看到 initializeGlide 中, 首先找尋 @GlideModule 注解生成類, 這里省略了它的實(shí)現(xiàn), 然后將一個(gè) RequestManagerFactory 添加到 GlideBuilder 內(nèi)部, 之后便構(gòu)建了一個(gè) Glide 的對(duì)象

我們主要關(guān)注一下 Glide 對(duì)象創(chuàng)建的過程

public final class GlideBuilder {
  // 管理線程池的引擎
  private Engine engine;
  // 1\. 線程池
  private GlideExecutor sourceExecutor;
  private GlideExecutor diskCacheExecutor;
  private GlideExecutor animationExecutor;
  // 2\. 內(nèi)存的緩存策略
  private MemorySizeCalculator memorySizeCalculator;
  private MemoryCache memoryCache;
  // 3\. 享元復(fù)用池
  private BitmapPool bitmapPool;
  private ArrayPool arrayPool;
  // 4\. 磁盤緩存和請(qǐng)求構(gòu)建工廠
  private DiskCache.Factory diskCacheFactory;
  private RequestManagerFactory requestManagerFactory;

  @NonNull
  Glide build(@NonNull Context context) {
    // 1.1 網(wǎng)絡(luò)操作使用線程池
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }
    // 1.2 磁盤緩存使用的線程池
    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
    // 1.3 執(zhí)行動(dòng)畫的線程池
    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }
    // 2.1 描述一個(gè)內(nèi)存的計(jì)算器, 智能加載圖片的大小, 判斷其需要的內(nèi)存空間
    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }
    // 2.2 資源緩存
    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
    // 3.1 Bitmap 復(fù)用池
    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }
    // 3.2 數(shù)組 復(fù)用池
    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }
    // 4.1 磁盤緩存的工廠
    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
    // 4.2 new 了一個(gè) RequestManagerRetriever 對(duì)象
    RequestManagerRetriever requestManagerRetriever =  new RequestManagerRetriever(requestManagerFactory);
    // 5\. 構(gòu)建了一個(gè)負(fù)責(zé)管理線程池與緩存的執(zhí)行引擎
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              GlideExecutor.newAnimationExecutor(),
              isActiveResourceRetentionAllowed);
    }
    ......
    // 6\. 創(chuàng)建了 Glide 對(duì)象
    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptions.lock(),
        defaultTransitionOptions,
        defaultRequestListeners,
        ......);
  }

}

public class Glide implements ComponentCallbacks2 {

  private final Registry registry;

  Glide(......) {
    // 6.1 將 Builder 中的線程池, 緩存池等保存
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;
    // 6.2 使用 registry 注冊(cè) Glide 需要的 Encoder 與 Decoder
    DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
    final Resources resources = context.getResources();
    registry = new Registry();
    registry.register(new DefaultImageHeaderParser());    
    ......
    // 6.3 構(gòu)建一個(gè) Glide 的上下文
    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptions,
            defaultTransitionOptions,
            defaultRequestListeners,
            engine,
            isLoggingRequestOriginsEnabled,
            logLevel);
}
""

好的, 可以看到 Glide 對(duì)象的構(gòu)建過程異常的復(fù)雜, 筆者調(diào)整了部分的數(shù)據(jù), 它們的流程如下

  • 構(gòu)建線程池
    • 根據(jù)不同的任務(wù)特性, 構(gòu)建了不同的線程池
  • 構(gòu)建內(nèi)存緩存策略
    • 內(nèi)存計(jì)算器
    • LRU 算法
  • 構(gòu)建對(duì)象復(fù)用池
  • 構(gòu)建工廠類
    • 創(chuàng)建了一個(gè) RequestManagerRetriever 對(duì)象
  • 構(gòu)建 Glide 執(zhí)行引擎
    • 用于組織線程池和緩存
  • 創(chuàng)建 Glide 對(duì)象
    • 將 GlideBuilder 中的數(shù)據(jù)導(dǎo)入
    • 構(gòu)建一個(gè) registry, 注冊(cè)了眾多的編解碼器
    • 構(gòu)建了一個(gè) GlideContext 對(duì)象, 描述其數(shù)據(jù)資源的上下文

從 Glide 構(gòu)造的流程中, 可以看到它主要有五個(gè)核心部分, 線程池, 內(nèi)存緩存策略, 對(duì)象復(fù)用策略和圖片音視頻的編解碼

在創(chuàng)建 GlideBuilder.build 中, 我們看到了它 new 了一個(gè) RequestManagerRetriever 對(duì)象并且傳遞到了 Glide 對(duì)象內(nèi)部, 于是通過 Glide.getRequestManagerRetriever 就可以很方便的獲取到 RequestManagerRetriever 這個(gè)對(duì)象了

獲取到了 RequestManagerRetriever 實(shí)例后, 接下來就可以通過 RequestManagerRetriever.get() 方法獲取 RequestManager 對(duì)象了

二) 獲取 RequestManager

public class RequestManagerRetriever implements Handler.Callback {

  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)) {
      // 1\. 若在主線程 并且 Context 不為 Application
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        // 不斷的查找其 BaseContext, 判斷是否能夠與 FragmentActivity/Activity 等目標(biāo)匹配
        return get(((ContextWrapper) context).getBaseContext());
      }
    } 
    // 2\. 若不在 MainThread 或 context 為 Application 的類型, 則使用 ApplicationManager
    return getApplicationManager(context);
  }
}
""

可以看到 RequestManagerRetriever.get 方法會(huì)判斷 Context 的類型

  • 若在主線程并且不為 Application 類型的 Context 則找尋其依賴的 Activity
  • 若非主線程或?yàn)?Application 類型的 Context, 則使用 ApplicationManager

為什么要優(yōu)先找到 Activity 呢, 這么做是何用意呢?, 我們帶著問題去看看參數(shù)為 Activity 的 get 的重載方法

1. 獲取 Activity 的 RequestManager

public class RequestManagerRetriever implements Handler.Callback { 

  public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      // 若非主線程, 直接獲取 Application 類型的 RequestManager
      return get(activity.getApplicationContext());
    } else {
      // 不可在 activity 銷毀時(shí)執(zhí)行加載
      assertNotDestroyed(activity);
      // 獲取其 FragmentManager
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

  private RequestManager fragmentGet(@NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    // 1\. 從 Activity 中獲取一個(gè) RequestManagerFragment, 用于監(jiān)管 Activity 的聲明周期
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    // 2\. 獲取 Fragment 中保存的當(dāng)前頁面的請(qǐng)求管理器 
    RequestManager requestManager = current.getRequestManager();
    // 3\. 不存在則創(chuàng)建一個(gè)請(qǐng)求管理器保存在 RequestManagerFragment 中
    if (requestManager == null) {
      Glide glide = Glide.get(context);
      requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    // 返回這個(gè)請(qǐng)求管理器
    return requestManager;
  }

  // 描述一個(gè)即將被 FragmentManager 添加的 RequestManagerFragment 緩存
  final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments =
      new HashMap<>();

  private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    // 2.1 嘗試從 FragmentManager 中獲取這個(gè) Fragment
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    // 2.2 不存在則添加一個(gè)
    if (current == null) {
      // 2.3 從 pendingRequestManagerFragments 緩存中獲取一個(gè)
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        // 2.3.1 創(chuàng)建并更新到緩存
        current = new RequestManagerFragment();
        ......
        // 2.3.2 添加到等待被添加的緩存中
        // 因?yàn)樘砑拥?FragmentManager 有延遲, 用這種方式防止同一時(shí)間創(chuàng)建了兩個(gè) RequestManagerFragment 對(duì)象添加到 Activity 中
        pendingRequestManagerFragments.put(fm, current);
        // 2.3.3 添加到 FragmentManager 中
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        // 2.3.4 添加到 FragmentManager 成功, 通過 Handler 移除這個(gè)緩存
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }  
}
""

好的, 可以看到 RequestManagerRetriever 的 get 方法主要是在 Activity 頁面中添加一個(gè) RequestManagerFragment 實(shí)例, 以便用于監(jiān)聽 Activity 的生命周期, 然后給這個(gè) Fragment 注入一個(gè) RequestManager, 其處理的細(xì)節(jié)代碼中也注釋的比較詳細(xì)

  • 其中有個(gè)非常引人注目的細(xì)節(jié), 考慮到將 FragmentManger 添加 Fragment 有延遲, 為了防止同一時(shí)間創(chuàng)建了兩個(gè) RequestManagerFragment 添加到 FragmentManager, 因此它使用了 pendingRequestManagerFragments 進(jìn)行緩存

2. 獲取 Application 的 RequestManager

public class RequestManagerRetriever implements Handler.Callback { 

  private volatile RequestManager applicationManager;
  private final RequestManagerFactory factory;

  private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager = factory.build(glide, new ApplicationLifecycle(),
              new EmptyRequestManagerTreeNode(), context.getApplicationContext());
        }
      }
    }
    return applicationManager;
  }  

}
""

很簡單構(gòu)建了一個(gè)單例的 RequestManager, 用于處理所有的 context 類型的請(qǐng)求

三) 回顧

至此 Glide.with 操作就完成了, 簡單的回顧一下 with 方法

  • 構(gòu)建 Glide 實(shí)例
  • 獲取 RequestManagerRetriever 對(duì)象
  • 構(gòu)建 RequestManager 對(duì)象
    • 若可以綁定 Activity, 則為 Activity 添加一個(gè) RequestManagerFragment, 其內(nèi)部含有 ReqeustManager 對(duì)象, 以便后續(xù)直接根據(jù) Activity 的生命周期管控 Glide 請(qǐng)求的處理
    • 若非可綁定 Activity, 則獲取一個(gè)單例的 applicationManager 專門用于處理這類請(qǐng)求
169e7953319d039b.jpg

好的, Glide.with 方法主要是為 Context 構(gòu)建其對(duì)應(yīng)的請(qǐng)求管理者, 接下來我們看看這個(gè) RequestManager.load 方法

二. RequestManager.load

我們使用最熟悉的加載網(wǎng)絡(luò)圖片來分析這個(gè) RequestManager.load 方法

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {

  public RequestBuilder<Drawable> load(@Nullable String string) {
    // 1\. 調(diào)用 asDrawable 創(chuàng)建一個(gè)目標(biāo)為 Drawable 的圖片加載請(qǐng)求
    // 2\. 調(diào)用 load 將要加載的資源傳入
    return asDrawable().load(string);
  }

  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }

}

好的, 可以看到 RequestManager.load 方法先調(diào)用了 asDrawable 構(gòu)建一個(gè) RequestBuilder, 描述一個(gè)目標(biāo)資源為 Drawable 的圖片加載請(qǐng)求

然后調(diào)用了 RequestBuilder.load 方法將加載的數(shù)據(jù)源傳入, 我們看看這個(gè) load 方法做了什么

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {

  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }

  // 描述加載的數(shù)據(jù)源
  @Nullable private Object model;
  // 描述這個(gè)請(qǐng)求是否已經(jīng)添加了加載的數(shù)據(jù)源
  private boolean isModelSet;

  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

}

好的, 可以看到走到這里, RequestBuilder 就構(gòu)建好了, 接下來就等待執(zhí)行這個(gè)請(qǐng)求了, 我們看看它的 RequestBuilder 的 into 方法

三. RequestBuilder.into

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {

  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    ......
    // 1\. 根據(jù) view 的 scaleType 重構(gòu) RequestOptions
    BaseRequestOptions<?> requestOptions = this;// RequestBuilder 直接繼承了 BaseRequestOptions
    if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { 
      switch (view.getScaleType()) {
        case CENTER_CROP:
          // 1.1 克隆原 RequestOptions, 配置一個(gè) CenterCrop 的縮放選項(xiàng)
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        ......
      }
    }
    // 2\. 調(diào)用 into 方法, 創(chuàng)建并且執(zhí)行請(qǐng)求
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

}
""

好的, 可以看到 into 方法中

  • 第一步是根據(jù) ImageView 的 ScaleType 來配置 Options 選項(xiàng)
  • 第二步調(diào)用了重載方法 into 執(zhí)行后續(xù)構(gòu)建請(qǐng)求操作

我們以 CenterCrop 舉例, 看看它如何配置縮放效果

一) 配置 Options

public abstract class BaseRequestOptions<T extends BaseRequestOptions<T>> implements Cloneable { 

  public T optionalCenterCrop() {
    // 1\. 調(diào)用了 optionalTransform
    // DownsampleStrategy 描述降采樣壓縮的策略
    // CenterCrop 描述圖像變化方式
    return optionalTransform(DownsampleStrategy.CENTER_OUTSIDE, new CenterCrop());
  }

  final T optionalTransform(@NonNull DownsampleStrategy downsampleStrategy,
      @NonNull Transformation<Bitmap> transformation) {
    ......
    // 2\. 將降采樣壓縮策略添加到 options 中
    downsample(downsampleStrategy);
    // 3\. 將圖像變化方式添加到 transformations 中
    return transform(transformation, /*isRequired=*/ false);
  }

  public T downsample(@NonNull DownsampleStrategy strategy) {
    // 2.1 調(diào)用了 set, 將降采樣策略保存到 options 中
    return set(DownsampleStrategy.OPTION, Preconditions.checkNotNull(strategy));
  }

  private Options options = new Options();

  public <Y> T set(@NonNull Option<Y> option, @NonNull Y value) {
    ...
    // 2.2 添加到 options 緩存中
    options.set(option, value);
    return selfOrThrowIfLocked();
  }

  T transform(@NonNull Transformation<Bitmap> transformation, boolean isRequired) {
    // 3.1 調(diào)用了 transform 的重載方法, 將這個(gè)圖像變化的方式作用到多種資源類型上
    DrawableTransformation drawableTransformation = new DrawableTransformation(transformation, isRequired);
    transform(Bitmap.class, transformation, isRequired);// Bitmap 類型的資源
    transform(Drawable.class, drawableTransformation, isRequired);// Drawable類型的
    ......
    return selfOrThrowIfLocked();
  }

  private Map<Class<?>, Transformation<?>> transformations = new CachedHashCodeArrayMap<>();

  <Y> T transform(@NonNull Class<Y> resourceClass, @NonNull Transformation<Y> transformation, boolean isRequired) {
    // 3.2 添加到了 transformations 緩存中
    transformations.put(resourceClass, transformation);
    return selfOrThrowIfLocked();
  }
}

好的, 可以看到配置縮放選項(xiàng)的操作除了添加了圖像變化操作, 還設(shè)定了采樣方式, 分別保存在 transformations 和 options 中

接下來我們看看請(qǐng)求的構(gòu)建

二) 構(gòu)建 Request 請(qǐng)求

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {

  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    ......
    // 2\. 調(diào)用 into 方法, 創(chuàng)建一個(gè) ViewTarget 對(duì)象
    return into(
        // 2.1 調(diào)用 GlideContext.buildImageViewTarget 構(gòu)建一個(gè) ViewTarget
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

}
""

可以看到在調(diào)用重載方法 into 之前, 顯示調(diào)用了 GlideContext.buildImageViewTarget 構(gòu)建了一個(gè) ViewTarget

我們知道 GlideContext 是在 Glide 對(duì)象構(gòu)造時(shí)一并創(chuàng)建的, 它是 Context 的裝飾者對(duì)象, 在 Application 類型的 Context 中, 添加了 Glide 相關(guān)的數(shù)據(jù), 我們先看看它是如何構(gòu)建 ViewTarget 的

public class GlideContext extends ContextWrapper {

  private final ImageViewTargetFactory imageViewTargetFactory;

  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    // 調(diào)用工廠類來創(chuàng)建一個(gè) imageView 的 ViewTarget
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }

}

public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
    // 根據(jù)目標(biāo)編碼的類型來創(chuàng)建不同的 ViewTarget 對(duì)象, 因?yàn)槲覀儧]有 asBitmap, 因此這里為 Drawable
    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);
    } else {
      ......
    }
  }
} 

好的可以看到 GlideContext 中通過工廠類創(chuàng)建了 ImageView 的 ViewTarget 的, 它描述的是圖像處理結(jié)束之后, 最終要作用到的 View 目標(biāo)

構(gòu)建好了 ViewTarge, 接下來就可以分析重載的 into 方法了, 看看它是如何構(gòu)建請(qǐng)求的

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {

  private <Y extends Target<TranscodeType>> Y into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options,  Executor callbackExecutor) {
    ......
    // 調(diào)用 buildRequest 構(gòu)建了一個(gè) Glide 請(qǐng)求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    ......// 處理這個(gè) ViewTarget 之前的請(qǐng)求與新請(qǐng)求的沖突
    // 給 ViewTarget 設(shè)置這個(gè) Glide 請(qǐng)求
    target.setRequest(request);
    // 調(diào)用了請(qǐng)求 RequestManager.track 方法執(zhí)行請(qǐng)求
    requestManager.track(target, request);
    return target;
  }

}
""

好的, 可以看到調(diào)用了 buildRequest 構(gòu)建了一個(gè) Glide 的請(qǐng)求, 其構(gòu)建過程也非常有意思, 最終最調(diào)用 SingleRequest.obtain 構(gòu)建一個(gè) Request 的實(shí)例對(duì)象, 之后便是調(diào)用 RequestManager.track 將其分發(fā)并執(zhí)行了

回顧

回顧一下上面的內(nèi)容, RequestBuilder.into 方法做的事情非常之多

  • 根據(jù) ImageView 構(gòu)建采樣壓縮和圖像變化的策略保存在 Options 和 Transform 中
  • 構(gòu)建 ViewTarget, 描述這個(gè)請(qǐng)求要作用的 View 對(duì)象
  • 構(gòu)建 Request 請(qǐng)求并執(zhí)行
169e795331b9974b.png

四. 獲取數(shù)據(jù)源

可以看到請(qǐng)求的分發(fā), 是交由 RequestManager 執(zhí)行的, 正好與它的命名相符, 起到了管理請(qǐng)求的作用, 接下來我們看看它的 track 方法是如何分發(fā)這個(gè) Request 請(qǐng)求的

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {

  private final RequestTracker requestTracker;

  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    ......
    // 1\. 執(zhí)行請(qǐng)求
    requestTracker.runRequest(request);
  }

}

public class RequestTracker {

  private final Set<Request> requests =
      Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  private final List<Request> pendingRequests = new ArrayList<>();

  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      // 2\. 調(diào)用 request.begin 執(zhí)行任務(wù)
      request.begin();
    } else {
      ......
    }
  }

}

public final class SingleRequest<R> implements Request,
    SizeReadyCallback,
    ResourceCallback,
    FactoryPools.Poolable {

  public synchronized void begin() {
    ......
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      // 3\. 表示尺寸準(zhǔn)備好了
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      ......
    }
    ......
  }

  private Engine engine;
  private int width;
  private int height;

  public synchronized void onSizeReady(int width, int height) {
    ......
    // 4\. 調(diào)用了 Engine.load 方法構(gòu)建任務(wù)
    loadStatus = engine.load(......);
    ......
  }

}
""

好的, 可以看到最終調(diào)用到了 onSizeReady 去構(gòu)建可執(zhí)行任務(wù), 接下來我們就分析這一過程

一) 任務(wù)的構(gòu)建

public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {

  private final Jobs jobs;

  public synchronized <R> LoadStatus load(...) {
    // 1\. 根據(jù)傳入的參數(shù), 構(gòu)建這個(gè)請(qǐng)求的 key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
    // 2\. 從緩存中查找 key 對(duì)應(yīng)的資源
    // 2.1 嘗試從 ActiveResources 緩存中查找這個(gè) key 的緩存
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      // 若緩存存在, 則直接回調(diào) onResourceReady 處理后續(xù)操作
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      .......
      return null;
    }
    // 2.2 嘗試從 LruResourceCache 中找尋這個(gè)資源 
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      // 回調(diào) onResourceReady 處理后續(xù)操作
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }
    // 3\. 從緩存中查找 key 對(duì)應(yīng)的任務(wù)
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      // 3.1 走到這里說明這個(gè)任務(wù)已經(jīng)正在執(zhí)行了, 無需再次構(gòu)建執(zhí)行
      current.addCallback(cb, callbackExecutor);
      ......
      // 返回加載狀態(tài)即可
      return new LoadStatus(cb, current);
    }
    // 3.2 走到這里, 說明是一個(gè)新的任務(wù)
    // 3.2.1 則構(gòu)建一個(gè)新的引擎任務(wù)
    EngineJob<R> engineJob = engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
    // 3.2.2 構(gòu)建解碼任務(wù)
    DecodeJob<R> decodeJob = decodeJobFactory.build(......, engineJob);
    // 3.2.3 添加到任務(wù)緩存
    jobs.put(key, engineJob);
    ......
    // 3.2.4 執(zhí)行任務(wù)
    engineJob.start(decodeJob);
    ......
  }

}
""

好的, 可以看到 Engine.load 中的事情非常的重要

  • 構(gòu)建這個(gè)請(qǐng)求的 key
  • 從緩存中查找 key 對(duì)應(yīng)的資源, 若存在直接回 onResourceReady 表示資源準(zhǔn)備好了
    • 從 ActiveResources 緩存中查找
    • 從 LruResourceCache 緩存中查找
  • 從緩存中查找 key 對(duì)應(yīng)的任務(wù)
    • 若存在則說明無需再次獲取資源
    • 構(gòu)建新的任務(wù)
      • 構(gòu)建引擎任務(wù) EngineJob
      • 引擎的任務(wù)為解碼任務(wù) DecodeJob
      • 將任務(wù)添加到緩存, 防止多次構(gòu)建
      • 執(zhí)行任務(wù)

好的, 可以看到內(nèi)存緩存的處理是在 Engine 中進(jìn)行的, 若兩個(gè)內(nèi)存緩存都沒有命中, 則會(huì)構(gòu)建任務(wù)并執(zhí)行, 接下來我們看看任務(wù)的執(zhí)行過程

二) 任務(wù)的執(zhí)行

class EngineJob<R> implements DecodeJob.Callback<R>,
    Poolable {

  private final GlideExecutor diskCacheExecutor;  
  private DecodeJob<R> decodeJob;

  public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    // 獲取線程池
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor : getActiveSourceExecutor();
    // 執(zhí)行任務(wù)
    executor.execute(decodeJob);
  }

}

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  @Override      
  public void run() {
     try {
      ......
      // 調(diào)用了 runWrapped
      runWrapped();
    } catch (CallbackException e) {
      ......
    }
  }        

  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        // 1\. 獲取任務(wù)的場景
        stage = getNextStage(Stage.INITIALIZE);
        // 2\. 獲取這個(gè)場景的執(zhí)行者
        currentGenerator = getNextGenerator();
        // 3\. 執(zhí)行者執(zhí)行任務(wù)
        runGenerators();
        break;
      ......
    }
  }

  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        // 1.1 若我們配置的緩存策略允許從 資源緩存 中讀數(shù)據(jù), 則返回 Stage.RESOURCE_CACHE
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        // 1.2 若我們配置的緩存策略允許從 源數(shù)據(jù)緩存中讀數(shù)據(jù), 則返回 Stage.DATA_CACHE
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // 1.3 若只能允許從緩存中獲取數(shù)據(jù), 則直接 FINISH, 否則返回 Stage.SOURCE, 意為加載一個(gè)新的資源
        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:
        // 資源磁盤緩存的執(zhí)行者
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        // 源數(shù)據(jù)磁盤緩存的執(zhí)行者
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        // 無緩存, 獲取數(shù)據(jù)的源的執(zhí)行者
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

  private void runGenerators() {
    ......
    boolean isStarted = false;
    // 調(diào)用 DataFetcherGenerator.startNext() 執(zhí)行了請(qǐng)求操作
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      // 若
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    ......
  }

}
""

DecodeJob 任務(wù)執(zhí)行時(shí), 它根據(jù)不同的場景, 獲取不同的場景執(zhí)行器, 然后調(diào)用了它們的 startNext 方法加載請(qǐng)求任務(wù)的數(shù)據(jù), 其映射表為

場景 場景描述 場景執(zhí)行器
Stage.RESOURCE_CACHE 從磁盤中緩存的資源中獲取數(shù)據(jù) ResourceCacheGenerator
Stage.DATA_CACHE 從磁盤中緩存的源數(shù)據(jù)中獲取數(shù)據(jù) DataCacheGenerator
Stage.SOURCE 重新請(qǐng)求數(shù)據(jù) SourceGenerator

我們知道在 Engine 中, 嘗試從內(nèi)存緩存中獲取資源, 而 DecodeJob 則是嘗試從磁盤緩存中獲取資源, 我們這里主要查看 SourceGenerator.startNext 是如何加載請(qǐng)求任務(wù)的數(shù)據(jù)的

三) 獲取源數(shù)據(jù)

class SourceGenerator implements DataFetcherGenerator,
    DataFetcher.DataCallback<Object>,
    DataFetcherGenerator.FetcherReadyCallback {

  private final DecodeHelper<?> helper;

  public boolean startNext() {
    ......
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      // 1\. 從 DecodeHelper 的數(shù)據(jù)加載集合中, 獲取一個(gè)數(shù)據(jù)加載器
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        // 2\. 使用加載器中 fetcher 執(zhí)行數(shù)據(jù)加載
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

}
""

好的, SourceGenerator 主要有兩步

  • 調(diào)用 DecodeHelper.getLoadData 獲取當(dāng)前請(qǐng)求的數(shù)據(jù)加載器
  • 調(diào)用加載器中的 fetcher.loadData 真正的執(zhí)行數(shù)據(jù)加載

1. 獲取數(shù)據(jù)加載器

final class DecodeHelper<Transcode> {

  private final List<LoadData<?>> loadData = new ArrayList<>();
  private GlideContext glideContext;
  private Object model;
  private boolean isLoadDataSet;    

  List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      // 1\. 從 Glide 注冊(cè)的 register 中獲取請(qǐng)求 model 加載器
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
      // 遍歷每一個(gè) modelLoaders 
      for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        // 2\. 通過 modelLoaders 構(gòu)建 loadData
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          // 添加到緩存
          loadData.add(current);
        }
      }
    }
    return loadData;
  }  

}
""

它會(huì)找到一個(gè) ModelLoader 的實(shí)現(xiàn)類, 通過這個(gè)實(shí)現(xiàn)類的 handles 方法, 判斷是否可以加載這個(gè) model
這里我們的 model 以網(wǎng)絡(luò)的 URL 資源舉例, 它的實(shí)現(xiàn)類為 HttpGlideUrlLoader 我們看看它如何構(gòu)建一個(gè) LoadData 對(duì)象的

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

  @Nullable private final ModelCache<GlideUrl, GlideUrl> modelCache;

  @Override
  public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
      @NonNull Options options) {  
    GlideUrl url = model;
    .....
    int timeout = options.get(TIMEOUT);
    // 創(chuàng)建了一個(gè) LoadData 對(duì)象, 并且實(shí)例化了一個(gè) HttpUrlFetcher 給它
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }

}
""

好的, 可以看到對(duì)于 URL 的加載, 其 fetcher 為一個(gè) HttpUrlFetcher 的實(shí)例, 接下來我們看看數(shù)據(jù)加載的流程

2. 執(zhí)行數(shù)據(jù)加載

獲取到了數(shù)據(jù)加載器之后, SourceGenerator 的 startNext 中便會(huì)調(diào)用其 fetcher 的 loadData 執(zhí)行數(shù)據(jù)的加載了, 我們結(jié)下來便分析一下這個(gè)過程

public class HttpUrlFetcher implements DataFetcher<InputStream> {

  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      // 獲取網(wǎng)絡(luò)圖片, 內(nèi)部使用了 HttpConnection 實(shí)現(xiàn), 僅僅做了重定向的處理
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      // 將 inputStream 回調(diào)出去
      callback.onDataReady(result);
    } catch (IOException e) {
      ......
      callback.onLoadFailed(e);
    } finally {
      ......
    }
  }

}
""

好的, 數(shù)據(jù)加載的過程也是很簡單的, HttpUrlFetcher 它使用了 HttpConnection 發(fā)起了網(wǎng)絡(luò)請(qǐng)求, 獲取了數(shù)據(jù)流, 至此數(shù)據(jù)資源的獲取就已經(jīng)完成了, 后面要做的便是最重要的數(shù)據(jù)處理了, 它通過回調(diào)的方式將 InputStream 扔了出去, 最終會(huì)回溯到 DecodeJob 的 onDataFetcherReady 這個(gè)方法中

四) 流程回顧

走到這里, 一個(gè)請(qǐng)求的數(shù)據(jù)源獲取就已經(jīng)完成, 還剩下對(duì)數(shù)據(jù)源的處理操作, 一次 Glide 數(shù)據(jù)加載就完成了, 我們先回顧一下這次加載的流程圖

  • 優(yōu)先從 memoryCache 中獲取
    • ActiveResource
    • LruResourceCache
  • 次優(yōu)先從 diskCache 中獲取
    • Resource 資源緩存
    • Data 源數(shù)據(jù)緩存
  • 執(zhí)行新的加載任務(wù)獲取源數(shù)據(jù)
    • 通過 SourceGenerator 獲取數(shù)據(jù)
    • 通過 HttpUrlFetcher 獲取網(wǎng)絡(luò)數(shù)據(jù)流
169e795331ee2f38.png

五. 數(shù)據(jù)源的處理

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  private Key currentSourceKey;
  private Object currentData;
  private DataSource currentDataSource;
  private DataFetcher<?> currentFetcher;

  @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;  // 保存數(shù)據(jù)的 key
    this.currentData = data;            // 保存數(shù)據(jù)實(shí)體
    this.currentFetcher = fetcher;      // 保存數(shù)據(jù)的獲取器
    this.currentDataSource = dataSource;// 數(shù)據(jù)來源: url 為 REMOTE 類型的枚舉, 表示從遠(yuǎn)程獲取
    ......
    if (Thread.currentThread() != currentThread) {
      ......
    } else {
      try {
        // 調(diào)用 decodeFromRetrievedData 解析獲取的數(shù)據(jù)
        decodeFromRetrievedData();
      } finally {
        ......
      }
    }
  }

  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      // 1\. 調(diào)用了 decodeFromData 獲取資源
      resource = decodeFromData(/*HttpUrlFetcher*/currentFetcher, /*InputStream*/currentData,/*REMOTE*/ currentDataSource);
    } catch (GlideException e) {
      ......
    }
    if (resource != null) {
      // 2\. 通知外界資源獲取成功了
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      ......
    }
  }

}
""

可以看到對(duì)于獲取到的數(shù)據(jù), 首先要將其解碼為 Resource 類型的資源, 然后再將資源返回給上層

我們先看看它是如何將數(shù)據(jù)解析成 Resource(非 Android 系統(tǒng)的 Resource) 資源的

一) 資源的獲取

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
      DataSource dataSource) throws GlideException {
    try {
      ......
      // 調(diào)用了 decodeFromFetcher
      Resource<R> result = decodeFromFetcher(data, dataSource);
      ......
      return result;
    } finally {
    }
  }

  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    // 1\. 獲取當(dāng)前數(shù)據(jù)類的解析器 LoadPath
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    // 2\. 通過解析器來解析來解析數(shù)據(jù)
    return runLoadPath(data, dataSource, path);
  }

  private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
      LoadPath<Data, ResourceType, R> path) throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    // 2.1 根據(jù)數(shù)據(jù)類型獲取一個(gè)數(shù)據(jù)重造器, 獲取的數(shù)據(jù)為 InputStream, 因此它是一個(gè) InputStreamRewinder 的實(shí)例
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // 2.2 將解析資源的任務(wù)轉(zhuǎn)移到了 LoadPath.load 方法中
      return path.load( rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }        
}
""

可以看到為了解析數(shù)據(jù), 首先構(gòu)建了一個(gè) LoadPath, 然后創(chuàng)建了一個(gè) InputStreamRewinder 類型的 DataRewinder, 最終將數(shù)據(jù)解析的操作到了 LoadPath.load 方法中

接下來看看這個(gè)LoadPath.load 做了哪些處理

public class LoadPath<Data, ResourceType, Transcode> {

 public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
      int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
    ......
    try {
      return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    } finally {
      ......
    }
  }

  private final List<? extends DecodePath<Data, ResourceType, Transcode>> decodePaths;

  private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
      @NonNull Options options, int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
      List<Throwable> exceptions) throws GlideException {
    Resource<Transcode> result = null;
    // 遍歷內(nèi)部存儲(chǔ)的 DecodePath 集合, 通過他們來解析數(shù)據(jù)
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        // 調(diào)用 DecodePath.decode 真正進(jìn)行數(shù)據(jù)的解析
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
        ......
      }
      ......
    }
    return result;
  }

}

public class DecodePath<DataType, ResourceType, Transcode> {

  public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
    // 1\. 調(diào)用 decodeResource 將源數(shù)據(jù)解析成中間資源
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    // 2\. 調(diào)用 DecodeCallback.onResourceDecoded 處理中間資源
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    // 3\. 調(diào)用 ResourceTranscoder.transcode 將中間資源轉(zhuǎn)為目標(biāo)資源
    return transcoder.transcode(transformed, options);
  }

  private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
      int height, @NonNull Options options) throws GlideException {
    try {
      // 1.1 調(diào)用了 decodeResourceWithList
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
      ......
    }
  }

  @NonNull
  private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
      int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
    Resource<ResourceType> result = null;
    for (int i = 0, size = decoders.size(); i < size; i++) {
      ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
      try {
        DataType data = rewinder.rewindAndGet();
        if (decoder.handles(data, options)) {
          data = rewinder.rewindAndGet();
          // 1.2 調(diào)用 ResourceDecoder.decode 解析源數(shù)據(jù)
          result = decoder.decode(data, width, height, options);
        }
      } 
      ......
      if (result != null) {
        break;
      }
    }
    return result;
  }

}
""

可以看到數(shù)據(jù)解析的任務(wù)最重是通過 DecodePath 來執(zhí)行的, 它內(nèi)部有三個(gè)操作

  • 調(diào)用 decodeResource 將源數(shù)據(jù)解析成資源
    • 源數(shù)據(jù): InputStream
    • 中間產(chǎn)物: Bitmap
  • 調(diào)用 DecodeCallback.onResourceDecoded 處理資源
  • 調(diào)用 ResourceTranscoder.transcode 將資源轉(zhuǎn)為目標(biāo)資源
    • 目標(biāo)資源類型: Drawable

1. 解析源數(shù)據(jù)

因?yàn)楸敬瘟鞒痰脑磾?shù)據(jù)為 InputStream 因此它的解析器為 StreamBitmapDecoder

public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {

  private final Downsampler downsampler;

  public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
      @NonNull Options options)
      throws IOException {
    ......
    try {
      // 根據(jù)請(qǐng)求配置的數(shù)據(jù), 對(duì)數(shù)據(jù)流進(jìn)行采樣壓縮, 獲取到一個(gè) Resource<Bitmap>
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      ......
    }
  }

}
""

可以看到它內(nèi)部通過 Downsampler.decode 方法對(duì)數(shù)據(jù)流進(jìn)行采樣壓縮, 來獲取這個(gè)流的 Bitmap

  • 這個(gè)采樣的策略就是我們?cè)跇?gòu)建 Request 時(shí)傳入的, 其采樣壓縮的細(xì)節(jié), 并不是我們本次關(guān)注的重點(diǎn)

我們看看獲取到了 Resource 之后, 如何處理這個(gè)資源

2. 資源的處理

可以看到, 當(dāng)我們將源數(shù)據(jù)解析成對(duì)應(yīng)的資源之后, 便會(huì)調(diào)用 DecodeCallback.onResourceDecoded 處理資源, 我們看看它的處理過程

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  private final class DecodeCallback<Z> implements DecodePath.DecodeCallback<Z> {

    @Override
    public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
      // 調(diào)用了外部類的 onResourceDecoded 方法
      return DecodeJob.this.onResourceDecoded(dataSource, decoded);
    }

  }

  private final DeferredEncodeManager<?> deferredEncodeManager = new DeferredEncodeManager<>();

  <Z> Resource<Z> onResourceDecoded(DataSource dataSource,
      @NonNull Resource<Z> decoded) {
    // 1\. 獲取數(shù)據(jù)資源的類型
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;

    // 2\. 若非從資源磁盤緩存中獲取的數(shù)據(jù)源, 則對(duì)資源進(jìn)行 transformation 操作
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    ......
    // 3\. 構(gòu)建數(shù)據(jù)編碼的策略
    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;
    }
    // 4\. 根據(jù)編碼策略, 構(gòu)建緩存的 key
    Resource<Z> result = transformed;
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
    if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
        encodeStrategy)) {
      ......
      final Key key;
      switch (encodeStrategy) {
        case SOURCE:
          // 源數(shù)據(jù)的 key
          key = new DataCacheKey(currentSourceKey, signature);
          break;
        case TRANSFORMED:
          // 資源數(shù)據(jù)的 key
          key =
              new ResourceCacheKey(......);
          break;
        default:
          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
      }
      // 5\. 初始化編碼管理者, 用于提交內(nèi)存緩存
      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
    // 返回 transform 之后的 bitmap
    return result;
  }

}
""

可以看到 onResourceDecoded 中, 主要是對(duì)中間資源做了如下的操作

  • 對(duì)資源進(jìn)行 transformed 操作
    • 將資源轉(zhuǎn)為目標(biāo)效果, 如在構(gòu)建 request 時(shí), 設(shè)置的 CenterCrop
  • 構(gòu)建磁盤緩存的 key

好的, 這個(gè)方法執(zhí)行結(jié)束之后, 這個(gè)資源就與我們期望的效果一致了, 接下來只需要將它轉(zhuǎn)為目標(biāo)格式就可以展示了

3. 將數(shù)據(jù)轉(zhuǎn)為目標(biāo)格式

目標(biāo)數(shù)據(jù)為 Drawable, 因此它的轉(zhuǎn)換器為 BitmapDrawableTranscoder

public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {

  private final Resources resources;

  @Nullable
  @Override
  public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
      @NonNull Options options) {
    // 調(diào)用了 LazyBitmapDrawableResource.obtain 獲取 Resource<BitmapDrawable> 的實(shí)例對(duì)象
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  }

}

public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
    Initializable {

  public static Resource<BitmapDrawable> obtain(
      @NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
    ......
    // 創(chuàng)建了一個(gè) LazyBitmapDrawableResource
    return new LazyBitmapDrawableResource(resources, bitmapResource);
  }

  private LazyBitmapDrawableResource(@NonNull Resources resources,
      @NonNull Resource<Bitmap> bitmapResource) {
    this.resources = Preconditions.checkNotNull(resources);
    this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
  }

  public BitmapDrawable get() {
    // Get 方法反回了一個(gè) BitmapDrawable 對(duì)象
    return new BitmapDrawable(resources, bitmapResource.get());
  }

}
""

好的, 轉(zhuǎn)化成目標(biāo)數(shù)據(jù)也非常的簡單, 它將我們解析到的 bitmap 存放到 LazyBitmapDrawableResource 內(nèi)部, 然后外界通過 get 方法就可以獲取到一個(gè) BitmapDrawable 的對(duì)象了

4. 解碼轉(zhuǎn)換的結(jié)構(gòu)圖

169e795331ed351f.png

二) 數(shù)據(jù)的展示

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {

  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    ......// 解析 inputStream 獲取資源 
    if (resource != null) {
      // 通知外界資源獲取成功了
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      ......
    }
  }

  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    ......
    // 1\. 回調(diào)上層資源準(zhǔn)備好了
    notifyComplete(result, dataSource);
    ......
    try {
      // 2\. 將數(shù)據(jù)緩存到磁盤
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      ...
    }
  }

  private Callback<R> callback;

  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    ......
    // 1.1 從 DecodeJob 的構(gòu)建中, 我們知道這個(gè) Callback 是一 EngineJob
    callback.onResourceReady(resource, dataSource);
  }

}
""

好的, 可以看到 DecodeJob.decodeFromRetrievedData 中, 主要做了兩個(gè)操作

  • 回調(diào) EngineJob.onResourceReady 資源準(zhǔn)備好了
  • 將數(shù)據(jù)緩存到磁盤

磁盤緩存并非我們關(guān)注的終點(diǎn), 這里我們看看 EngineJob.onResourceReady 中做了哪些處理

class EngineJob<R> implements DecodeJob.Callback<R>,
    Poolable {

  @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    synchronized (this) {
      this.resource = resource;
      this.dataSource = dataSource;
    }
    notifyCallbacksOfResult();
  } 

  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;
    }
    // 1\. 通知上層 Engine 任務(wù)完成了
    listener.onEngineJobComplete(this, localKey, localResource);
    // 2\. 回調(diào)給 ImageViewTarget 展示資源
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
  }

}

""

好的, EngineJob 中也是有兩步操作, 一個(gè)是通知上層任務(wù)完成了, 另一個(gè)是回調(diào)給 ImageViewTarget 展示資源

我們先看看上層做了什么處理

public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {

  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    if (resource != null) {
      // 將加載好的資源添加到內(nèi)存緩存
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }
    ......
  }

}
""

我們知道在請(qǐng)求發(fā)起前是 Engine 嘗試通過內(nèi)存緩存讀, 結(jié)束之后再回到 Engine 添加內(nèi)存緩存也不足為奇了

接下來我們看看 ImageViewTarget 展示資源的過程

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
    implements Transition.ViewAdapter {

  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    // 處理一些 transition 變化, 在構(gòu)建 Request 時(shí)有分析過, 這里不贅述其實(shí)現(xiàn)細(xì)節(jié)了
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      ......
    }
  }

  private void setResourceInternal(@Nullable Z resource) {
    // 調(diào)用了 setResource
    setResource(resource);
    ......
  }

}

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

  protected void setResource(@Nullable Drawable resource) {
    // 呈現(xiàn)到 View 上
    view.setImageDrawable(resource);
  }  

}
""

ImageViewTarget 調(diào)用了子類重寫的 setResource 方法, 將數(shù)據(jù)填充進(jìn)去, 至此一次 Glide 圖像加載就完成了

三) 流程回顧

169e79533213afb233.jpg

六. 總結(jié)

通過一次流程分析我們得知, 整個(gè) Glide 圖片加載主要有如下幾步

  • 請(qǐng)求管理器的構(gòu)建
    • 一個(gè) Context 對(duì)應(yīng)一個(gè) RequestManager
  • 請(qǐng)求的構(gòu)建
    • 請(qǐng)求的寬高钮莲、采樣的方式、transform 變化...
  • 通過請(qǐng)求獲取資源
    • Engine 從內(nèi)存緩存中查找
      • 從 ActiveResources 緩存中查找
      • 從 LruResourceCache 緩存中查找
    • 內(nèi)存緩存不存在, 則構(gòu)建任務(wù)執(zhí)行
      • 構(gòu)建一個(gè) EngineJob 描述一個(gè)請(qǐng)求任務(wù), 任務(wù)類型為 DecodeJob
        • DecodeJob 從 diskCache 中查找
        • diskCache 不存在, 則通過網(wǎng)絡(luò)請(qǐng)求, 獲取數(shù)據(jù)源
        • 通過 Downsampler 解析源數(shù)據(jù)并進(jìn)行采樣壓縮獲取 Bitmap
        • 對(duì) Bitmap 進(jìn)行 transform 處理
          • 構(gòu)建磁盤緩存的 key
        • 將 transform 之后的 Bitmap 轉(zhuǎn)為 Resource 回傳給上層
          • DecodeJob 進(jìn)行磁盤緩存
    • Engine 對(duì)資源進(jìn)行內(nèi)存緩存
  • 傳遞給 View 進(jìn)行展示

看了 Glide 的加載流程, 我似乎能夠明白為什么他是 Google 推薦的圖片加載框架了, 內(nèi)部細(xì)節(jié)的處理做的非常的到位, 而且使用 GlideContext 用于描述 Glide 的上下文, 與 Android 的 Context 巧妙的融合在一起, 讀起來真有一種閱讀 Android 源碼的既視感

不過這只是最簡單的流程, 而且 Glide 支持 Gif, 視頻加載操作, 可想而知其內(nèi)部的 Decorder 處理了多少邏輯代碼, 如此復(fù)雜的流程, 嵌套了如此之多的回調(diào), 無疑增加了我們閱讀源碼的難度, 若是將這些操作分層, 并且使用攔截器去實(shí)現(xiàn), 我想定會(huì)讓一次圖像加載操作變得更加清晰明了
(轉(zhuǎn)載文章:https://juejin.im/post/5ca5c7f7e51d45430235ba03#heading-12

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖朋魔,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卿操,居然都是意外死亡警检,警方通過查閱死者的電腦和手機(jī)孙援,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扇雕,“玉大人拓售,你說我怎么就攤上這事∠夥睿” “怎么了础淤?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長腮鞍。 經(jīng)常有香客問我值骇,道長,這世上最難降的妖魔是什么移国? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任吱瘩,我火速辦了婚禮,結(jié)果婚禮上迹缀,老公的妹妹穿的比我還像新娘使碾。我一直安慰自己,他們只是感情好祝懂,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布票摇。 她就那樣靜靜地躺著,像睡著了一般砚蓬。 火紅的嫁衣襯著肌膚如雪矢门。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天灰蛙,我揣著相機(jī)與錄音祟剔,去河邊找鬼。 笑死摩梧,一個(gè)胖子當(dāng)著我的面吹牛物延,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播仅父,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼叛薯,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了笙纤?” 一聲冷哼從身側(cè)響起耗溜,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎省容,沒想到半個(gè)月后抖拴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蓉冈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年城舞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寞酿。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡家夺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伐弹,到底是詐尸還是另有隱情拉馋,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布惨好,位于F島的核電站煌茴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏日川。R本人自食惡果不足惜蔓腐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望龄句。 院中可真熱鬧回论,春花似錦、人聲如沸分歇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽职抡。三九已至葬燎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缚甩,已是汗流浹背谱净。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹄胰,地道東北人岳遥。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像裕寨,于是被迫代替她去往敵國和親浩蓉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容