glide流程梳理

glide現在已經4.x了, 就以該版本的源碼進行分析了.

glide優勢

  • 通過Fragment來監聽頁面生命週期來控制圖片的加載與取消;
  • 使用@GlideModel的方式可動態註冊、替換圖片加載器;
  • glide初始化時註冊加載器、轉碼器、編碼器等;
  • Target通過ViewTreeObserver來監聽控件的寬高提供給BitmapFactory.decodeStream()來解碼原始流;
  • 四級緩存, 活躍緩存、內存緩存、硬盤緩存、網絡加載;
  • 可以返回多種數據格式Drawable、Bitmap.

Glide.with(this)

initializeGlide()

解碼資源的接口(如將File, InputStream etc 解碼爲Bitmap, Drawable etc):  public interface ResourceDecoder<T, Z> ;

將數據編碼寫入持久性數據存儲區(如本地文件緩存)的接口: public interface Encoder<T> ;

將一種類型的資源轉碼爲另一種類型的資源(如Resources轉BitmapDrawable): public interface ResourceTranscoder<Z, R>  ;

將任意複雜的數據模型轉換爲具體的數據類型的工廠接口(如請求網絡獲取原始流): public interface ModelLoader<Model, Data>;

  • encoderRegistry.append(dataClass, encoder);            數據編碼存儲註冊
  • decoderRegistry.append(bucket/**解碼器的存儲桶標識符*/, decoder/**已註冊的ResourceDecoder*/, dataClass/**被解碼的數據(比如流)*/, resourceClass/**解碼結果數據(比如Bitmap)*/);  解碼器註冊
  • modelLoaderRegistry.append(modelClass/**模型類(如URL, file path)*/, dataClass/**返回的數據類型(如InputStream)*/, factory/**模型加載工廠類*/);                                                               模型加載器註冊
  • transcoderRegistry.register(resourceClass/**將被轉碼的類型(如Bitmap)*/, transcodeClass/**轉碼後的類型(如BitmapDrawable)*/, transcoder/**已註冊的資源轉碼器*/);                            轉碼器註冊
  • RequestManagerRetriever.ID_REMOVE_SUPPORT_FRAGMENT_MANAGER 存在的作用???
  • RequestManagerRetriever.supportFragmentGet() 中實現將RequestManager註冊到SupportRequestManagerFragment的ActivityFragmentLifecycle屬性中來感知Fragment的生命週期來進行圖片的加載與取消.
  • 反射獲取業務方@GlideModel註解的類;
  • manifest解析
  • 排除manifest下被註解配置的model
  • 引用傳遞參數給自定義模塊

RequestManager.load(url)

 /** 
  * 指定ResourceType爲Drawable, transcodeClass爲Drawable.class 
  */
 public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }


  /**
   * 結合上面方法:load(url)後 model類型爲String, transcodeClass爲Drawable.class
   */
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

生命週期綁定

傳入的不同類型的參數Fragment、Activity、Context、Application創建一個沒有界面的 RequestManagerFragment , 返回 RequestManager 對象,  爲了綁定生命週期將 RequestManagerFragment 的屬性 ActivityFragmentLifecycle 傳遞到 RequestManager, 讓 RequestManager 將自己註冊到 ActivityFragmentLifecycle 的觀察者集合, 從而在 RequestManagerFragment 的生命週期方法中通知事件訂閱者. 構造 RequestManager 對象時初始化了 RequestManager.glide 屬性 , 此時 glide的屬性, 註冊不同格式數據源的編碼、解碼器對象, 不同數據來源的加載器對象, 圖片對象變換器, 編碼轉換器等被初始化.

資源加載器、轉換器、編碼器、變換器、內存緩存、硬盤緩存、active緩存對象的初始化

    // glide對象的屬性
    private Engine engine;
    private BitmapPool bitmapPool;
    private MemoryCache memoryCache;          // LruResourceCache 原數據內存緩存
    private ExecutorService sourceService;    // FIFO 網絡請求線程池
    private ExecutorService diskCacheService; // FIFO 磁盤操作線程池
    private DecodeFormat decodeFormat;
    private DiskCache.Factory diskCacheFactory;  // 磁盤操作接口類DiskCache


    // 爲 Engine 類的屬性, 構造glide對象時初始化glide.engine屬性
    private final Map<Key, EngineJob> jobs;
    private final EngineKeyFactory keyFactory;
    private final MemoryCache cache;
    private final EngineJobFactory engineJobFactory;
    private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
    private final ResourceRecycler resourceRecycler;
    private final LazyDiskCacheProvider diskCacheProvider;
RequestManager.load(url)

根據as(Drawable.class)參數類型Drawable.class構建RequestBuilder(Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context)對象,  繼續構造this.mode = url; 返回RequestBuilder對象.

RequestBuilder.into(imageView)

轉換爲

RequestBuilder.into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);

其中glideContext是在Glide的構造方法中初始化的,  使用工廠模式根據參數的classType從ImageViewTargetFactory構造一個(ViewTarget<ImageView, Z>)DrawableImageViewTarget對象, 請求類SingleRequest, 請求池類 Pools.Pool<SingleRequest<?>>, 在調用engine.load()傳入了this作爲回調接口ResourceCallback, 


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

  void track(@NonNull Target<?> target, @NonNull Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

執行網絡請求的地方

  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

真正的加載是在Engine.load()中的DecodeJob.run();

  public <R> LoadStatus load(...) {

    EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);
    // 活躍緩存
    EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      return null;
    }
    // 內存緩存
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      return null;
    }

    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb);
      return new LoadStatus(cb, current);
    }
    EngineJob<R> engineJob = engineJobFactory.build(...);
    DecodeJob<R> decodeJob = decodeJobFactory.build(..., engineJob);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(decodeJob);
 }

runWrapped()

  private void runWrapped() {
    switch (runReason) { // 初始化爲INITIALIZE
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE); // 遞歸獲取值爲 Stage.SOURCE
        currentGenerator = getNextGenerator(); // 返回 new SourceGenerator(decodeHelper, this);
        runGenerators();                       /** 執行SourceGenerator.startNext()下載網絡圖片 */
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }

 

  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

 

  private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
      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);
    }
  }

 

加載數據的入口

使用OkHttp加載數據並回調

 @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {

    if (Thread.currentThread() != currentThread) {
      runReason = RunReason.DECODE_DATA;
      callback.reschedule(this);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }

解碼和轉碼

  private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }

從註冊的解碼器集合中查找能處理原數據的解碼器

 

解碼

解碼變換完成切換線程

String類型的url默認是HttpUrlFetcher.loadData()加載圖片, 回調是SourceGenerator.onDataReady()

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

 

從網絡加載圖片數據OkHttpStreamFetcher

硬盤緩存加載時序圖

測試的時候硬盤緩存策略使用的是DiskCacheStrategy.DATA

  public static final DiskCacheStrategy DATA = new DiskCacheStrategy() {
    @Override
    public boolean isDataCacheable(DataSource dataSource) {
      return dataSource != DataSource.DATA_DISK_CACHE && dataSource != DataSource.MEMORY_CACHE;
    }

    @Override
    public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
        EncodeStrategy encodeStrategy) {
      return false;
    }

    @Override
    public boolean decodeCachedResource() {
      return false;
    }

    @Override
    public boolean decodeCachedData() {
      return true;
    }
  };
  private Stage getNextStage(Stage current) {
    switch (current) {
      case INITIALIZE:
        return diskCacheStrategy.decodeCachedResource()
            ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
      case RESOURCE_CACHE:
        return diskCacheStrategy.decodeCachedData()
            ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
      case DATA_CACHE:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
  }

 

解轉碼流程

緩存相關類

硬盤緩存相關類:  DiskLruCacheFactory、 DiskLruCacheWrapper、InternalCacheDiskCacheFactory

硬盤緩存讀取  DiskLruCacheWrapper.get() 的調用方: 

DataCacheGenerator.startNext() 嘗試先從磁盤緩存中讀取, 未獲取到再從網絡讀取;

ResourceCacheGenerator.startNext() 嘗試先從磁盤緩存中讀取, 未獲取到再從網絡讀取.

硬盤緩存存儲  DiskLruCacheWrapper.put() 的調用方: 

DecodeJo$DeferredEncodeManager.encode()存儲到硬盤

內存緩存相關類: LruResourceCache、ActiveResources

ActiveResources.activate()的調用方:

Engine.load()  -> Engine.loadFromCache() ;

Engine.onEngineJobComplete().

ActiveResources.get()的調用方:

Engine.load()  -> Engine.loadFromActiveResources()

ActiveResources.deactivate()的調用方:

Engine.onResourceReleased()

LruResourceCache.remove()的調用方:

Engine.load() -> Engine.loadFromCache() -> Engine.getEngineResourceFromCache()

LruResourceCache.put()的調用方:

Engine.onResourceReleased();

Engine.cache.setResourceRemovedListener(Engine.this);

BitmapPool相關類: LruBitmapPool

變化工具類: TransformationUtils

 

SimpleTarget的使用追蹤

DownsampleStrategy內置了幾個DownsampleStrategy.CenterOutside

    // sourceWidth     服務器端返回的圖片寬度
    // requestedWidth  UI控件的寬度
    // 返回縮放百分比的較大值
    public float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth,
        int requestedHeight) {
      float widthPercentage = requestedWidth / (float) sourceWidth;
      float heightPercentage = requestedHeight / (float) sourceHeight;
      return Math.max(widthPercentage, heightPercentage);
    }

關鍵問題代碼

  int targetWidth = requestedWidth == Target.SIZE_ORIGINAL ? sourceWidth : requestedWidth;
  int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight;

具體的縮放因子與ImageView控件的scaleType有關, 參考類DownsampleStrategy

options.inTargetDensity > 0 && options.inDensity 

Glide.with(MainActivity.this)
        .load(url)
        .into(imageView);

分析一下調用後此處圖片的寬高和控件的寬高:

RequestBuilder.into()中通過工廠類ImageViewTargetFactory.buildTarget()獲取DrawableImageViewTarget對象;

執行SingleRequest.begin()的   

if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
    onSizeReady(overrideWidth, overrideHeight);
} else {
    target.getSize(this);
}

調用的是ViewTarget.getSize(), 其實是調用的ViewTarget$SizeDeterminer.getSize(),核心是     

 if (layoutListener == null) {
     ViewTreeObserver observer = view.getViewTreeObserver();
     layoutListener = new SizeDeterminerLayoutListener(this);
     observer.addOnPreDrawListener(layoutListener);
 }

private static final class SizeDeterminerLayoutListener
        implements ViewTreeObserver.OnPreDrawListener {
      private final WeakReference<SizeDeterminer> sizeDeterminerRef;

      SizeDeterminerLayoutListener(@NonNull SizeDeterminer sizeDeterminer) {
        sizeDeterminerRef = new WeakReference<>(sizeDeterminer);
      }

      @Override
      public boolean onPreDraw() {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
          Log.v(TAG, "OnGlobalLayoutListener called attachStateListener=" + this);
        }
        SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
        if (sizeDeterminer != null) {
          sizeDeterminer.checkCurrentDimens();
        }
        return true;
      }
}

再回到SingleRequest.onSizeReady()繼續執行. 到執行InputStream解碼的時候, 會根據控件的scaleType、寬高和返回的圖片流數據中的寬高做縮放, 使用了Option.inSample屬性.

 

下次分析:

每次圖片加載都new 一個Engine?

 

參考文檔 

官網介紹: http://bumptech.github.io/glide/doc/transformations.html

詳細blog: https://blog.csdn.net/yanfeivip8/article/details/50418064

郭霖blog: https://blog.csdn.net/guolin_blog/article/details/53759439

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章