Glide 4.9 原始碼分析(一) —— 一次完整載入流程

SharryChoo發表於2019-04-04

前言

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

// 這裡便是與 Glide 3+ 的不同
RequestOptions options = new RequestOptions()
        .placeholder(R.drawable.loading);
// 它需要一個
Glide.with(this)
     .load(url)
     .apply(options)
     .into(imageView);
複製程式碼

好的, 可以看到 Glide 的使用方式極為簡單, 但往往越是簡單的背後, 越是隱藏了複雜的實現, 接下來我們就一步一步的分析 Glide 4.9 的一次載入流程

一. Glide.with

public class Glide implements ComponentCallbacks2 {
    
  public static RequestManager with(@NonNull Context context) {
    // 1. 呼叫了 getRetriever 獲取一個 RequestManagerRetriever
    // 2. 呼叫了 RequestManagerRetriever.get 獲取一個 RequestManager 描述一個圖片載入請求的管理者
    return getRetriever(context).get(context);
  }
    
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // 1.1 呼叫了 Glide.get 獲取 Glide 物件
    // 1.2 通過 Glide 物件獲取一個 RequestManagerRetriever
    // 這個 Retriever 用來獲取一個 RequestManager 物件, 可以參考 Android Framework 原始碼中的 SystemRetriever
    return Glide.get(context).getRequestManagerRetriever();
  }
}
複製程式碼

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

  • 通過 Glide.getRetriever 獲取一個 RequestManagerRetriever 物件, 它描述為請求管理的獲取器
    • 呼叫 Glide.get 獲取 Glide 物件
    • 呼叫 Glide.getRequestManagerRetriever, 獲取 RequestManagerRetriever 物件
  • 呼叫 getRequestManagerRetriever.get 獲取一個 RequestManager

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

一) 獲取 RequestManagerRetriever

從上面的分析可只, RequestManagerRetriever 是通過 Glide.getRequestManagerRetriever 獲取到的, 因此需要先獲取 Glide 物件的例項, 因此我們先看看這個 Glide 是如何構造的

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;
    // 進行初始化操作
    initializeGlide(context);
    isInitializing = false;
  }
  
  private static void initializeGlide(@NonNull Context context) {
    // 建立了一個 GlideBuilder() 例項傳入 initializeGlide, 很顯然是為了初始化 Glide 物件
    // 接下來我們就看看它是如何初始化 Glide 的
    initializeGlide(context, new GlideBuilder());
  }

  private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    // 1. 獲取 @GlideModule 註解驅動生成的 GeneratedAppGlideModuleImpl 和 GeneratedAppGlideModuleFactory 類
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    ......
    // 2. 嘗試從註解生成的 annotationGeneratedModule 中獲取 RequestManager 的構造工廠物件
    RequestManagerRetriever.RequestManagerFactory factory = annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
    // 3. 向 Glide 的 Builder 中新增這個請求管理器的構造工廠
    builder.setRequestManagerFactory(factory);
    ......
    // 4. 構建 Glide 的實體物件
    Glide glide = builder.build(applicationContext);
    ......
    // 5. 向 Application 中註冊一個元件的回撥, 用於檢測系統 Config 改變和記憶體佔用量低的訊號
    applicationContext.registerComponentCallbacks(glide);
    // 儲存在靜態的成員變數中
    Glide.glide = glide;
  }
}
複製程式碼

好的, 可以看到 initializeGlide 中, 首先找尋 @GlideModule 註解生成類, 這裡省略了它的實現, 然後將一個 RequestManagerFactory 新增到 GlideBuilder 內部, 之後便構建了一個 Glide 的物件

我們主要關注一下 Glide 物件建立的過程

public final class GlideBuilder {
  // 管理執行緒池的引擎
  private Engine engine;
  // 1. 執行緒池
  private GlideExecutor sourceExecutor;
  private GlideExecutor diskCacheExecutor;
  private GlideExecutor animationExecutor;
  // 2. 記憶體的快取策略
  private MemorySizeCalculator memorySizeCalculator;
  private MemoryCache memoryCache;
  // 3. 享元複用池
  private BitmapPool bitmapPool;
  private ArrayPool arrayPool;
  // 4. 磁碟快取和請求構建工廠
  private DiskCache.Factory diskCacheFactory;
  private RequestManagerFactory requestManagerFactory;
 
  @NonNull
  Glide build(@NonNull Context context) {
    // 1.1 網路操作使用執行緒池
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }
    // 1.2 磁碟快取使用的執行緒池
    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
    // 1.3 執行動畫的執行緒池
    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }
    // 2.1 描述一個記憶體的計算器, 智慧載入圖片的大小, 判斷其需要的記憶體空間
    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }
    // 2.2 資源快取
    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
    // 3.1 Bitmap 複用池
    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }
    // 3.2 陣列 複用池
    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }
    // 4.1 磁碟快取的工廠
    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
    // 4.2 new 了一個 RequestManagerRetriever 物件
    RequestManagerRetriever requestManagerRetriever =  new RequestManagerRetriever(requestManagerFactory);
    // 5. 構建了一個負責管理執行緒池與快取的執行引擎
    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              GlideExecutor.newAnimationExecutor(),
              isActiveResourceRetentionAllowed);
    }
    ......
    // 6. 建立了 Glide 物件
    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 註冊 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 構建一個 Glide 的上下文
    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptions,
            defaultTransitionOptions,
            defaultRequestListeners,
            engine,
            isLoggingRequestOriginsEnabled,
            logLevel);
}
複製程式碼

好的, 可以看到 Glide 物件的構建過程異常的複雜, 筆者調整了部分的資料, 它們的流程如下

  • 構建執行緒池
    • 根據不同的任務特性, 構建了不同的執行緒池
  • 構建記憶體快取策略
    • 記憶體計算器
    • LRU 演算法
  • 構建物件複用池
  • 構建工廠類
    • 建立了一個 RequestManagerRetriever 物件
  • 構建 Glide 執行引擎
    • 用於組織執行緒池和快取
  • 建立 Glide 物件
    • 將 GlideBuilder 中的資料匯入
    • 構建一個 registry, 註冊了眾多的編解碼器
    • 構建了一個 GlideContext 物件, 描述其資料資源的上下文

從 Glide 構造的流程中, 可以看到它主要有五個核心部分, 執行緒池, 記憶體快取策略, 物件複用策略和圖片音視訊的編解碼

在建立 GlideBuilder.build 中, 我們看到了它 new 了一個 RequestManagerRetriever 物件並且傳遞到了 Glide 物件內部, 於是通過 Glide.getRequestManagerRetriever 就可以很方便的獲取到 RequestManagerRetriever 這個物件了

獲取到了 RequestManagerRetriever 例項後, 接下來就可以通過 RequestManagerRetriever.get() 方法獲取 RequestManager 物件了

二) 獲取 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 等目標匹配
        return get(((ContextWrapper) context).getBaseContext());
      }
    } 
    // 2. 若不在 MainThread 或 context 為 Application 的型別, 則使用 ApplicationManager
    return getApplicationManager(context);
  }
}
複製程式碼

可以看到 RequestManagerRetriever.get 方法會判斷 Context 的型別

  • 若在主執行緒並且不為 Application 型別的 Context 則找尋其依賴的 Activity
  • 若非主執行緒或為 Application 型別的 Context, 則使用 ApplicationManager

為什麼要優先找到 Activity 呢, 這麼做是何用意呢?, 我們帶著問題去看看引數為 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 銷燬時執行載入
      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 中獲取一個 RequestManagerFragment, 用於監管 Activity 的宣告週期
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
    // 2. 獲取 Fragment 中儲存的當前頁面的請求管理器 
    RequestManager requestManager = current.getRequestManager();
    // 3. 不存在則建立一個請求管理器儲存在 RequestManagerFragment 中
    if (requestManager == null) {
      Glide glide = Glide.get(context);
      requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    // 返回這個請求管理器
    return requestManager;
  }
  
  // 描述一個即將被 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 中獲取這個 Fragment
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    // 2.2 不存在則新增一個
    if (current == null) {
      // 2.3 從 pendingRequestManagerFragments 快取中獲取一個
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        // 2.3.1 建立並更新到快取
        current = new RequestManagerFragment();
        ......
        // 2.3.2 新增到等待被新增的快取中
        // 因為新增到 FragmentManager 有延遲, 用這種方式防止同一時間建立了兩個 RequestManagerFragment 物件新增到 Activity 中
        pendingRequestManagerFragments.put(fm, current);
        // 2.3.3 新增到 FragmentManager 中
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        // 2.3.4 新增到 FragmentManager 成功, 通過 Handler 移除這個快取
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }  
}
複製程式碼

好的, 可以看到 RequestManagerRetriever 的 get 方法主要是在 Activity 頁面中新增一個 RequestManagerFragment 例項, 以便用於監聽 Activity 的生命週期, 然後給這個 Fragment 注入一個 RequestManager, 其處理的細節程式碼中也註釋的比較詳細

  • 其中有個非常引人注目的細節, 考慮到將 FragmentManger 新增 Fragment 有延遲, 為了防止同一時間建立了兩個 RequestManagerFragment 新增到 FragmentManager, 因此它使用了 pendingRequestManagerFragments 進行快取

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;
  }  
    
}
複製程式碼

很簡單構建了一個單例的 RequestManager, 用於處理所有的 context 型別的請求

三) 回顧

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

  • 構建 Glide 例項
  • 獲取 RequestManagerRetriever 物件
  • 構建 RequestManager 物件
    • 若可以繫結 Activity, 則為 Activity 新增一個 RequestManagerFragment, 其內部含有 ReqeustManager 物件, 以便後續直接根據 Activity 的生命週期管控 Glide 請求的處理
    • 若非可繫結 Activity, 則獲取一個單例的 applicationManager 專門用於處理這類請求

Glide 請求組織關係圖

好的, Glide.with 方法主要是為 Context 構建其對應的請求管理者, 接下來我們看看這個 RequestManager.load 方法

二. RequestManager.load

我們使用最熟悉的載入網路圖片來分析這個 RequestManager.load 方法

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
        
  public RequestBuilder<Drawable> load(@Nullable String string) {
    // 1. 呼叫 asDrawable 建立一個目標為 Drawable 的圖片載入請求
    // 2. 呼叫 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 方法先呼叫了 asDrawable 構建一個 RequestBuilder, 描述一個目標資源為 Drawable 的圖片載入請求

然後呼叫了 RequestBuilder.load 方法將載入的資料來源傳入, 我們看看這個 load 方法做了什麼

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
        
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  
  // 描述載入的資料來源
  @Nullable private Object model;
  // 描述這個請求是否已經新增了載入的資料來源
  private boolean isModelSet;
  
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

}
複製程式碼

好的, 可以看到走到這裡, RequestBuilder 就構建好了, 接下來就等待執行這個請求了, 我們看看它的 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. 根據 view 的 scaleType 重構 RequestOptions
    BaseRequestOptions<?> requestOptions = this;// RequestBuilder 直接繼承了 BaseRequestOptions
    if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { 
      switch (view.getScaleType()) {
        case CENTER_CROP:
          // 1.1 克隆原 RequestOptions, 配置一個 CenterCrop 的縮放選項
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        ......
      }
    }
    // 2. 呼叫 into 方法, 建立並且執行請求
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

}
複製程式碼

好的, 可以看到 into 方法中

  • 第一步是根據 ImageView 的 ScaleType 來配置 Options 選項
  • 第二步呼叫了過載方法 into 執行後續構建請求操作

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

一) 配置 Options

public abstract class BaseRequestOptions<T extends BaseRequestOptions<T>> implements Cloneable { 
    
  public T optionalCenterCrop() {
    // 1. 呼叫了 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 呼叫了 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 呼叫了 transform 的過載方法, 將這個影象變化的方式作用到多種資源型別上
    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();
  }
}
複製程式碼

好的, 可以看到配置縮放選項的操作除了新增了影象變化操作, 還設定了取樣方式, 分別儲存在 transformations 和 options 中

接下來我們看看請求的構建

二) 構建 Request 請求

public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
    implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
    
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    ......
    // 2. 呼叫 into 方法, 建立一個 ViewTarget 物件
    return into(
        // 2.1 呼叫 GlideContext.buildImageViewTarget 構建一個 ViewTarget
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }
        
}
複製程式碼

可以看到在呼叫過載方法 into 之前, 顯示呼叫了 GlideContext.buildImageViewTarget 構建了一個 ViewTarget

我們知道 GlideContext 是在 Glide 物件構造時一併建立的, 它是 Context 的裝飾者物件, 在 Application 型別的 Context 中, 新增了 Glide 相關的資料, 我們先看看它是如何構建 ViewTarget 的

public class GlideContext extends ContextWrapper {
  
  private final ImageViewTargetFactory imageViewTargetFactory;

  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    // 呼叫工廠類來建立一個 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) {
    // 根據目標編碼的型別來建立不同的 ViewTarget 物件, 因為我們沒有 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 中通過工廠類建立了 ImageView 的 ViewTarget 的, 它描述的是影象處理結束之後, 最終要作用到的 View 目標

構建好了 ViewTarge, 接下來就可以分析過載的 into 方法了, 看看它是如何構建請求的

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) {
    ......
    // 呼叫 buildRequest 構建了一個 Glide 請求
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    ......// 處理這個 ViewTarget 之前的請求與新請求的衝突
    // 給 ViewTarget 設定這個 Glide 請求
    target.setRequest(request);
    // 呼叫了請求 RequestManager.track 方法執行請求
    requestManager.track(target, request);
    return target;
  }
          
}
複製程式碼

好的, 可以看到呼叫了 buildRequest 構建了一個 Glide 的請求, 其構建過程也非常有意思, 最終最呼叫 SingleRequest.obtain 構建一個 Request 的例項物件, 之後便是呼叫 RequestManager.track 將其分發並執行了

回顧

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

  • 根據 ImageView 構建取樣壓縮和影象變化的策略儲存在 Options 和 Transform 中
  • 構建 ViewTarget, 描述這個請求要作用的 View 物件
  • 構建 Request 請求並執行

Glide 請求與目標的關係圖

四. 獲取資料來源

可以看到請求的分發, 是交由 RequestManager 執行的, 正好與它的命名相符, 起到了管理請求的作用, 接下來我們看看它的 track 方法是如何分發這個 Request 請求的

public class RequestManager implements LifecycleListener,
    ModelTypes<RequestBuilder<Drawable>> {
  
  private final RequestTracker requestTracker;
    
  synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    ......
    // 1. 執行請求
    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. 呼叫 request.begin 執行任務
      request.begin();
    } else {
      ......
    }
  }
  
}

public final class SingleRequest<R> implements Request,
    SizeReadyCallback,
    ResourceCallback,
    FactoryPools.Poolable {
  
  public synchronized void begin() {
    ......
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      // 3. 表示尺寸準備好了
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      ......
    }
    ......
  }
  
  private Engine engine;
  private int width;
  private int height;
  
  public synchronized void onSizeReady(int width, int height) {
    ......
    // 4. 呼叫了 Engine.load 方法構建任務
    loadStatus = engine.load(......);
    ......
  }
          
}
複製程式碼

好的, 可以看到最終呼叫到了 onSizeReady 去構建可執行任務, 接下來我們就分析這一過程

一) 任務的構建

public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {
  
  private final Jobs jobs;

  public synchronized <R> LoadStatus load(...) {
    // 1. 根據傳入的引數, 構建這個請求的 key
    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
    // 2. 從快取中查詢 key 對應的資源
    // 2.1 嘗試從 ActiveResources 快取中查詢這個 key 的快取
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      // 若快取存在, 則直接回撥 onResourceReady 處理後續操作
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      .......
      return null;
    }
    // 2.2 嘗試從 LruResourceCache 中找尋這個資源 
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      // 回撥 onResourceReady 處理後續操作
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }
    // 3. 從快取中查詢 key 對應的任務
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      // 3.1 走到這裡說明這個任務已經正在執行了, 無需再次構建執行
      current.addCallback(cb, callbackExecutor);
      ......
      // 返回載入狀態即可
      return new LoadStatus(cb, current);
    }
    // 3.2 走到這裡, 說明是一個新的任務
    // 3.2.1 則構建一個新的引擎任務
    EngineJob<R> engineJob = engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
    // 3.2.2 構建解碼任務
    DecodeJob<R> decodeJob = decodeJobFactory.build(......, engineJob);
    // 3.2.3 新增到任務快取
    jobs.put(key, engineJob);
    ......
    // 3.2.4 執行任務
    engineJob.start(decodeJob);
    ......
  }
    
}
複製程式碼

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

  • 構建這個請求的 key
  • 從快取中查詢 key 對應的資源, 若存在直接回 onResourceReady 表示資源準備好了
    • 從 ActiveResources 快取中查詢
    • 從 LruResourceCache 快取中查詢
  • 從快取中查詢 key 對應的任務
    • 若存在則說明無需再次獲取資源
    • 構建新的任務
      • 構建引擎任務 EngineJob
      • 引擎的任務為解碼任務 DecodeJob
      • 將任務新增到快取, 防止多次構建
      • 執行任務

好的, 可以看到記憶體快取的處理是在 Engine 中進行的, 若兩個記憶體快取都沒有命中, 則會構建任務並執行, 接下來我們看看任務的執行過程

二) 任務的執行

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();
    // 執行任務
    executor.execute(decodeJob);
  }
  
}

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
    Runnable,
    Comparable<DecodeJob<?>>,
    Poolable {
    
  @Override      
  public void run() {
     try {
      ......
      // 呼叫了 runWrapped
      runWrapped();
    } catch (CallbackException e) {
      ......
    }
  }        
  
  private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        // 1. 獲取任務的場景
        stage = getNextStage(Stage.INITIALIZE);
        // 2. 獲取這個場景的執行者
        currentGenerator = getNextGenerator();
        // 3. 執行者執行任務
        runGenerators();
        break;
      ......
    }
  }
  
  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        // 1.1 若我們配置的快取策略允許從 資源快取 中讀資料, 則返回 Stage.RESOURCE_CACHE
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        // 1.2 若我們配置的快取策略允許從 源資料快取中讀資料, 則返回 Stage.DATA_CACHE
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // 1.3 若只能允許從快取中獲取資料, 則直接 FINISH, 否則返回 Stage.SOURCE, 意為載入一個新的資源
        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);
      case DATA_CACHE:
        // 源資料磁碟快取的執行者
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
        // 無快取, 獲取資料的源的執行者
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }
  
  private void runGenerators() {
    ......
    boolean isStarted = false;
    // 呼叫 DataFetcherGenerator.startNext() 執行了請求操作
    while (!isCancelled && currentGenerator != null
        && !(isStarted = currentGenerator.startNext())) {
      // 若
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();
      if (stage == Stage.SOURCE) {
        reschedule();
        return;
      }
    }
    ......
  }
  
}
複製程式碼

DecodeJob 任務執行時, 它根據不同的場景, 獲取不同的場景執行器, 然後呼叫了它們的 startNext 方法載入請求任務的資料, 其對映表為

場景 場景描述 場景執行器
Stage.RESOURCE_CACHE 從磁碟中快取的資源中獲取資料 ResourceCacheGenerator
Stage.DATA_CACHE 從磁碟中快取的源資料中獲取資料 DataCacheGenerator
Stage.SOURCE 重新請求資料 SourceGenerator

我們知道在 Engine 中, 嘗試從記憶體快取中獲取資源, 而 DecodeJob 則是嘗試從磁碟快取中獲取資源, 我們這裡主要檢視 SourceGenerator.startNext 是如何載入請求任務的資料的

三) 獲取源資料

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 的資料載入集合中, 獲取一個資料載入器
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        // 2. 使用載入器中 fetcher 執行資料載入
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }
  
}
複製程式碼

好的, SourceGenerator 主要有兩步

  • 呼叫 DecodeHelper.getLoadData 獲取當前請求的資料載入器
  • 呼叫載入器中的 fetcher.loadData 真正的執行資料載入

1. 獲取資料載入器

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 註冊的 register 中獲取請求 model 載入器
      List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
      // 遍歷每一個 modelLoaders 
      for (int i = 0, size = modelLoaders.size(); i < size; i++) {
        // 2. 通過 modelLoaders 構建 loadData
        ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
        LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          // 新增到快取
          loadData.add(current);
        }
      }
    }
    return loadData;
  }  
    
}
複製程式碼

它會找到一個 ModelLoader 的實現類, 通過這個實現類的 handles 方法, 判斷是否可以載入這個 model
這裡我們的 model 以網路的 URL 資源舉例, 它的實現類為 HttpGlideUrlLoader 我們看看它如何構建一個 LoadData 物件的

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);
    // 建立了一個 LoadData 物件, 並且例項化了一個 HttpUrlFetcher 給它
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }
    
}
複製程式碼

好的, 可以看到對於 URL 的載入, 其 fetcher 為一個 HttpUrlFetcher 的例項, 接下來我們看看資料載入的流程

2. 執行資料載入

獲取到了資料載入器之後, SourceGenerator 的 startNext 中便會呼叫其 fetcher 的 loadData 執行資料的載入了, 我們結下來便分析一下這個過程

public class HttpUrlFetcher implements DataFetcher<InputStream> {
    
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      // 獲取網路圖片, 內部使用了 HttpConnection 實現, 僅僅做了重定向的處理
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      // 將 inputStream 回撥出去
      callback.onDataReady(result);
    } catch (IOException e) {
      ......
      callback.onLoadFailed(e);
    } finally {
      ......
    }
  }
    
}
複製程式碼

好的, 資料載入的過程也是很簡單的, HttpUrlFetcher 它使用了 HttpConnection 發起了網路請求, 獲取了資料流, 至此資料資源的獲取就已經完成了, 後面要做的便是最重要的資料處理了, 它通過回撥的方式將 InputStream 扔了出去, 最終會回溯到 DecodeJob 的 onDataFetcherReady 這個方法中

四) 流程回顧

走到這裡, 一個請求的資料來源獲取就已經完成, 還剩下對資料來源的處理操作, 一次 Glide 資料載入就完成了, 我們先回顧一下這次載入的流程圖

  • 優先從 memoryCache 中獲取
    • ActiveResource
    • LruResourceCache
  • 次優先從 diskCache 中獲取
    • Resource 資源快取
    • Data 源資料快取
  • 執行新的載入任務獲取源資料
    • 通過 SourceGenerator 獲取資料
    • 通過 HttpUrlFetcher 獲取網路資料流

Glide獲取源資料

五. 資料來源的處理

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;  // 儲存資料的 key
    this.currentData = data;            // 儲存資料實體
    this.currentFetcher = fetcher;      // 儲存資料的獲取器
    this.currentDataSource = dataSource;// 資料來源: url 為 REMOTE 型別的列舉, 表示從遠端獲取
    ......
    if (Thread.currentThread() != currentThread) {
      ......
    } else {
      try {
        // 呼叫 decodeFromRetrievedData 解析獲取的資料
        decodeFromRetrievedData();
      } finally {
        ......
      }
    }
  }
  
  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      // 1. 呼叫了 decodeFromData 獲取資源
      resource = decodeFromData(/*HttpUrlFetcher*/currentFetcher, /*InputStream*/currentData,/*REMOTE*/ currentDataSource);
    } catch (GlideException e) {
      ......
    }
    if (resource != null) {
      // 2. 通知外界資源獲取成功了
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      ......
    }
  }
  
}
複製程式碼

可以看到對於獲取到的資料, 首先要將其解碼為 Resource 型別的資源, 然後再將資源返回給上層

我們先看看它是如何將資料解析成 Resource(非 Android 系統的 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 {
      ......
      // 呼叫了 decodeFromFetcher
      Resource<R> result = decodeFromFetcher(data, dataSource);
      ......
      return result;
    } finally {
    }
  }
  
  private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    // 1. 獲取當前資料類的解析器 LoadPath
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    // 2. 通過解析器來解析來解析資料
    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 根據資料型別獲取一個資料重造器, 獲取的資料為 InputStream, 因此它是一個 InputStreamRewinder 的例項
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // 2.2 將解析資源的任務轉移到了 LoadPath.load 方法中
      return path.load( rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
  }        
}
複製程式碼

可以看到為了解析資料, 首先構建了一個 LoadPath, 然後建立了一個 InputStreamRewinder 型別的 DataRewinder, 最終將資料解析的操作到了 LoadPath.load 方法中

接下來看看這個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;
    // 遍歷內部儲存的 DecodePath 集合, 通過他們來解析資料
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        // 呼叫 DecodePath.decode 真正進行資料的解析
        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. 呼叫 decodeResource 將源資料解析成中間資源
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    // 2. 呼叫 DecodeCallback.onResourceDecoded 處理中間資源
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    // 3. 呼叫 ResourceTranscoder.transcode 將中間資源轉為目標資源
    return transcoder.transcode(transformed, options);
  }
  
  private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
      int height, @NonNull Options options) throws GlideException {
    try {
      // 1.1 呼叫了 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 呼叫 ResourceDecoder.decode 解析源資料
          result = decoder.decode(data, width, height, options);
        }
      } 
      ......
      if (result != null) {
        break;
      }
    }
    return result;
  }
    
}
複製程式碼

可以看到資料解析的任務最重是通過 DecodePath 來執行的, 它內部有三個操作

  • 呼叫 decodeResource 將源資料解析成資源
    • 源資料: InputStream
    • 中間產物: Bitmap
  • 呼叫 DecodeCallback.onResourceDecoded 處理資源
  • 呼叫 ResourceTranscoder.transcode 將資源轉為目標資源
    • 目標資源型別: Drawable

1. 解析源資料

因為本次流程的源資料為 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 {
      // 根據請求配置的資料, 對資料流進行取樣壓縮, 獲取到一個 Resource<Bitmap>
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      ......
    }
  }
    
}
複製程式碼

可以看到它內部通過 Downsampler.decode 方法對資料流進行取樣壓縮, 來獲取這個流的 Bitmap

  • 這個取樣的策略就是我們在構建 Request 時傳入的, 其取樣壓縮的細節, 並不是我們本次關注的重點

我們看看獲取到了 Resource 之後, 如何處理這個資源

2. 資源的處理

可以看到, 當我們將源資料解析成對應的資源之後, 便會呼叫 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) {
      // 呼叫了外部類的 onResourceDecoded 方法
      return DecodeJob.this.onResourceDecoded(dataSource, decoded);
    }
    
  }
  
  private final DeferredEncodeManager<?> deferredEncodeManager = new DeferredEncodeManager<>();

  <Z> Resource<Z> onResourceDecoded(DataSource dataSource,
      @NonNull Resource<Z> decoded) {
    // 1. 獲取資料資源的型別
    Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
    Transformation<Z> appliedTransformation = null;
    Resource<Z> transformed = decoded;
    
    // 2. 若非從資源磁碟快取中獲取的資料來源, 則對資源進行 transformation 操作
    if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
      appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
      transformed = appliedTransformation.transform(glideContext, decoded, width, height);
    }
    ......
    // 3. 構建資料編碼的策略
    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. 根據編碼策略, 構建快取的 key
    Resource<Z> result = transformed;
    boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
    if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
        encodeStrategy)) {
      ......
      final Key key;
      switch (encodeStrategy) {
        case SOURCE:
          // 源資料的 key
          key = new DataCacheKey(currentSourceKey, signature);
          break;
        case TRANSFORMED:
          // 資源資料的 key
          key =
              new ResourceCacheKey(......);
          break;
        default:
          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
      }
      // 5. 初始化編碼管理者, 用於提交記憶體快取
      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
    // 返回 transform 之後的 bitmap
    return result;
  }
        
}
複製程式碼

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

  • 對資源進行 transformed 操作
    • 將資源轉為目標效果, 如在構建 request 時, 設定的 CenterCrop
  • 構建磁碟快取的 key

好的, 這個方法執行結束之後, 這個資源就與我們期望的效果一致了, 接下來只需要將它轉為目標格式就可以展示了

3. 將資料轉為目標格式

目標資料為 Drawable, 因此它的轉換器為 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) {
    // 呼叫了 LazyBitmapDrawableResource.obtain 獲取 Resource<BitmapDrawable> 的例項物件
    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) {
    ......
    // 建立了一個 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 方法反回了一個 BitmapDrawable 物件
    return new BitmapDrawable(resources, bitmapResource.get());
  }

}
複製程式碼

好的, 轉化成目標資料也非常的簡單, 它將我們解析到的 bitmap 存放到 LazyBitmapDrawableResource 內部, 然後外界通過 get 方法就可以獲取到一個 BitmapDrawable 的物件了

4. 解碼轉換的結構圖

解碼轉換的結構圖

二) 資料的展示

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. 回撥上層資源準備好了
    notifyComplete(result, dataSource);
    ......
    try {
      // 2. 將資料快取到磁碟
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      ...
    }
  }
  
  private Callback<R> callback;
  
  private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    ......
    // 1.1 從 DecodeJob 的構建中, 我們知道這個 Callback 是一 EngineJob
    callback.onResourceReady(resource, dataSource);
  }
  
}
複製程式碼

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

  • 回撥 EngineJob.onResourceReady 資源準備好了
  • 將資料快取到磁碟

磁碟快取並非我們關注的終點, 這裡我們看看 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 任務完成了
    listener.onEngineJobComplete(this, localKey, localResource);
    // 2. 回撥給 ImageViewTarget 展示資源
    for (final ResourceCallbackAndExecutor entry : copy) {
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
  }
    
}

複製程式碼

好的, EngineJob 中也是有兩步操作, 一個是通知上層任務完成了, 另一個是回撥給 ImageViewTarget 展示資源

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

public class Engine implements EngineJobListener,
    MemoryCache.ResourceRemovedListener,
    EngineResource.ResourceListener {
  
  public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    if (resource != null) {
      // 將載入好的資源新增到記憶體快取
      if (resource.isCacheable()) {
        activeResources.activate(key, resource);
      }
    }
    ......
  }
     
}
複製程式碼

我們知道在請求發起前是 Engine 嘗試通過記憶體快取讀, 結束之後再回到 Engine 新增記憶體快取也不足為奇了

接下來我們看看 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 變化, 在構建 Request 時有分析過, 這裡不贅述其實現細節了
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      ......
    }
  }
  
  private void setResourceInternal(@Nullable Z resource) {
    // 呼叫了 setResource
    setResource(resource);
    ......
  }
 
}

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
    
  protected void setResource(@Nullable Drawable resource) {
    // 呈現到 View 上
    view.setImageDrawable(resource);
  }  
    
}
複製程式碼

ImageViewTarget 呼叫了子類重寫的 setResource 方法, 將資料填充進去, 至此一次 Glide 影象載入就完成了

三) 流程回顧

資源回撥

六. 總結

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

  • 請求管理器的構建
    • 一個 Context 對應一個 RequestManager
  • 請求的構建
    • 請求的寬高、取樣的方式、transform 變化...
  • 通過請求獲取資源
    • Engine 從記憶體快取中查詢
      • 從 ActiveResources 快取中查詢
      • 從 LruResourceCache 快取中查詢
    • 記憶體快取不存在, 則構建任務執行
      • 構建一個 EngineJob 描述一個請求任務, 任務型別為 DecodeJob
        • DecodeJob 從 diskCache 中查詢
        • diskCache 不存在, 則通過網路請求, 獲取資料來源
        • 通過 Downsampler 解析源資料並進行取樣壓縮獲取 Bitmap
        • 對 Bitmap 進行 transform 處理
          • 構建磁碟快取的 key
        • 將 transform 之後的 Bitmap 轉為 Resource 回傳給上層
          • DecodeJob 進行磁碟快取
    • Engine 對資源進行記憶體快取
  • 傳遞給 View 進行展示

看了 Glide 的載入流程, 我似乎能夠明白為什麼他是 Google 推薦的圖片載入框架了, 內部細節的處理做的非常的到位, 而且使用 GlideContext 用於描述 Glide 的上下文, 與 Android 的 Context 巧妙的融合在一起, 讀起來真有一種閱讀 Android 原始碼的既視感

不過這只是最簡單的流程, 而且 Glide 支援 Gif, 視訊載入操作, 可想而知其內部的 Decorder 處理了多少邏輯程式碼, 如此複雜的流程, 巢狀了如此之多的回撥, 無疑增加了我們閱讀原始碼的難度, 若是將這些操作分層, 並且使用攔截器去實現, 我想定會讓一次影象載入操作變得更加清晰明瞭

若想了解 Glide 取樣壓縮獲取 Bitmap 的實現詳情, 請檢視下面這篇文章 juejin.im/post/5ca5c8…

相關文章