Glide 源碼學習,了解 Glide 圖片加載原理

基于 Gilde 4.3.1
Glide 是我非常喜歡使用的圖片加載框架比吭,這篇文章講從源碼的角度剖析 Glide 框架祈坠。
從而得知 Glide 為我們做了哪些工作双抽。

Glide 的使用參考文檔

    ImageView imageView = findViewById(R.id.test);
    ImgurGlide.with(getApplicationContext())
            .load(imageUrl)
            .into(imageView);

Glide 使用起來特別方便宣虾,一條鏈式調用就可以把圖片下載并顯示到 ImageView 上祈匙。
其中 ImgurGlide 是我們自定義的一個 AppGlideModule

@GlideModule(glideName = "ImgurGlide")
public class ImgurGlideModule extends AppGlideModule {
    // Intentionally Empty.
}

ImgurGlide 的生成魏保,使用了 APT(Annotation Processing Tool)技術,這里先不做講述漂坏。ImgurGlide 的每個方法都是包裹了 Glide 靜態(tài)對象去實現(xiàn)景埃。

逐步分析

Glide.with(……)

Glide.with() 有下面幾種實現(xiàn)方式。

1. Glide.with(Context context)
2. Glide.with(Activity activity)
3. Glide.with(FragmentActivity activity)
4. Glide.with(android.app.Fragment fragment)
5. Glide.with(View view)

所以的方法實現(xiàn)也是很類似顶别,都是調用同一個方法

public static RequestManager with(Fragment fragment) {
  return getRetriever(fragment.getActivity()).get(fragment);
}

再看一下 getRetriever() 方法

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  ……
  省略一些判空檢查
  ——
  return Glide.get(context).getRequestManagerRetriever();
}

其中 Glide.get(context) 主要用來初始化 Glide 的全局單利對象谷徙,以及一些配置。

getRequestManagerRetriever() 則是返回 Glide 對象的 requestManagerRetriever 對象驯绎。

然后看一下 requestManagerRetriever.get() 方法

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

  return getApplicationManager(context);
}

get() 方法會根據(jù)傳入的 context 對象和當前線程完慧,創(chuàng)建不同的 RequestManager 實例

1. 非 UI 線程,返回 applicationManager 對象剩失,能感知 Application 生命周期屈尼。
2. UI 線程册着,如果 context 是 Activity 、FragmentActivity 
   則會創(chuàng)建一個能感知對應 Activity 的 RequestManager脾歧。
3. UI 線程甲捏,如果 Context 是 Fragment、android.support.v4.app.Fragment 
   則會創(chuàng)建一個能感知對應 Fragment 生命周期 的 RequestManager鞭执。

這里反復提到了一個 感知生命 xx 周期司顿,也是 Glide 的一個特性。

Glide 在加載資源的時候兄纺,如果是在 Activity大溜、Fragment 這一類有生命周期的組件上進行。
當 Activity估脆、Fragment 等組件進入不可見钦奋,或者已經(jīng)銷毀的時候,Glide 會停止加載資源疙赠。
Application 的生命周期貫穿整個應用锨苏,所以 applicationManager 只有在應用程序關閉的時候終止加載。

所以盡量不要在非 UI 線程使用 Glide 加載圖片棺聊,盡量使用 Activity、Fragment 等帶有生命周期的組件配合 Glide 使用贞谓。

Glide 如何獲得 Activity限佩、Fragment 生命周期回調

在 各種 requestManagerRetriever.get() 方法中如果傳入的是帶有生命周期的組件,并且在 UI 線程裸弦,會執(zhí)行以下幾個方法端

  // FragmentActivity
  assertNotDestroyed(activity);
  FragmentManager fm = activity.getSupportFragmentManager();
  return supportFragmentGet(activity, fm, null /*parentHint*/);
  
  //android.support.v4.app.Fragment
  FragmentManager fm = fragment.getChildFragmentManager();
  return supportFragmentGet(fragment.getActivity(), fm, fragment);
  
  //Activity
  assertNotDestroyed(activity);
  android.app.FragmentManager fm = activity.getFragmentManager();
  return fragmentGet(activity, fm, null /*parentHint*/);
  
  //android.app.Fragment
  android.app.FragmentManager fm = fragment.getChildFragmentManager();
  return fragmentGet(fragment.getActivity(), fm, fragment);
  1. 如果是 Activity 祟同,先獲取 FragmentManager ,如果是 Fragment 則先獲取 ChildFragmentManager理疙。
  2. 如果是 support 包下面的 Activity 晕城、Fragment 調用 supportFragmentGet,否則調用 fragmentGet窖贤。

fragmentGet() 和 supportFragmentGet() 方法大致類似砖顷,選取一個分析一下。

private RequestManager fragmentGet(Context context, android.app.FragmentManager fm,
    android.app.Fragment parentHint) {
  RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
  RequestManager requestManager = current.getRequestManager();
  if (requestManager == null) {
    // TODO(b/27524013): Factor out this Glide.get() call.
    Glide glide = Glide.get(context);
    requestManager =
        factory.build(
            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

上面這段代碼做了兩個功能

 1. 創(chuàng)建一個 RequestManagerFragment赃梧。
 2. 創(chuàng)建一個 RequestManager滤蝠。

先看一下 getRequestManagerFragment

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(
    final android.app.FragmentManager fm, android.app.Fragment parentHint) {
  RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
  if (current == null) {
    current = pendingRequestManagerFragments.get(fm);
    if (current == null) {
      current = new RequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      pendingRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}

這是是 Glide 設計中比較一個巧妙的地方

創(chuàng)建一個透明的 RequestManagerFragment 加入到FragmentManager 之中
通過添加的這個 Fragment 感知 Activity 、Fragment 的生命周期授嘀。

Fragment 源碼學習,從源碼理解 Fragment 生命周期中已經(jīng)說明物咳,添加到 Activity的 Fragment 會跟隨Activity的生命周期。

Fragment的 childFragment 則會通過 ChildFragmentManager 和 Fragment 保持生命周期一致蹄皱。

這里說一個細節(jié)

剛開始看這段代碼時览闰,看到 handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();

    ……
    case ID_REMOVE_FRAGMENT_MANAGER:
    android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
    key = fm;
    removed = pendingRequestManagerFragments.remove(fm);
    ……
以為 Glide 添加了一個 Fragment 到 FragmentManager 中芯肤,然后又刪除了。為此困惑了好久压鉴。
在 Glide 的 github 里 issue#2289 才明白崖咨。
   
   Glide 在添加 Fragment 到 FragmentManger 后,
   再從 pendingRequestManagerFragments 中刪除 FragmentManager 晴弃。
   并不是刪除 Fragment掩幢。

在 RequestManagerFragment 中可以看到以下代碼

@Override
public void onStart() {
  super.onStart();
  lifecycle.onStart();
}

@Override
public void onStop() {
  super.onStop();
  lifecycle.onStop();
}

@Override
public void onDestroy() {
  super.onDestroy();
  lifecycle.onDestroy();
  unregisterFragmentWithRoot();
}

專業(yè)就可以通過 RequestManagerFragment 把 Activity 的生命周期通過 lifecycle 傳遞給在 lifecycle 注冊的 LifecycleListener。

RequestManager.load(url)

public RequestBuilder<Drawable> load(@Nullable Object model) {
  return asDrawable().load(model);
}

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

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

public RequestBuilder<TranscodeType> load(@Nullable Object model) {
  return loadGeneric(model);
}

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

以上就是 RequestBuilder.load(url) 的相關代碼上鞠,發(fā)現(xiàn)并沒有什么特殊之處际邻。 只是創(chuàng)建了一個 RequestBuilder 。

RequestBuilder.into(view)

into() 方法調用起來十分方便芍阎,只要傳遞一個 ImageView 世曾,Glide 就會自動下載圖片,并且顯示到 ImageView 上谴咸。這看似十分簡單的一步轮听,也是 Glide 最負責的調用。

public Target<TranscodeType> into(ImageView view) {
  
  RequestOptions requestOptions = this.requestOptions;
  ……
  
  return into(
      glideContext.buildImageViewTarget(view, transcodeClass),
      /*targetListener=*/ null,
      requestOptions);
}

跟蹤一下 glideContext.buildImageViewTarget(view, transcodeClass) 會發(fā)現(xiàn)這里返回的是一個DrawableImageViewTarget

into(ImageView view) 把 requestOptions 和 DrawableImageViewTarget 傳入

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    RequestOptions options) {
  ……
  Request request = buildRequest(target, targetListener, options);

  Request previous = target.getRequest();
  ……
  requestManager.clear(target);
  target.setRequest(request);
  requestManager.track(target, request);

  return target;
}

下一步跟蹤到 requestManager.track(target, request)

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

public void runRequest(Request request) {
  requests.add(request);
  if (!isPaused) {
    request.begin();
  } else {
    pendingRequests.add(request);
  }
}  

isPaused 變量

1. 如果此時 GlideRequests 的 Lifecycle 為 ApplicationLifecycle岭佳,只要應用存活
   isPaused 為 false 血巍,直接執(zhí)行 request.begin()
2. 如果 GlideRequests 的 Lifecycle 是觀測 Fragment 或者 Activity
   isPaused 為true ,不會立即執(zhí)行 request.begin()
   當 Fragment 或者 Activity 顯示到前臺時通過遍歷 requests 數(shù)組執(zhí)行 request.begin()

所以執(zhí)行網(wǎng)絡請求下載圖片的操作在 request.begin() 之中。

回到 into(ImageView view) 方法的

Request request = buildRequest(target, targetListener, options)

經(jīng)過層層包裹我們可以找到一下路線珊随,發(fā)現(xiàn)最后返回的是 SingleRequest

buildRequest >> buildRequestRecursive >> buildThumbnailRequestRecursive

>> obtainRequest >> SingleRequest

創(chuàng)建 SingleRequest 的過程比較復雜述寡,牽扯到縮略圖、錯誤處理之類的邏輯叶洞,大致都是上面那條路徑鲫凶。

然后就看一下 SingleRequest.begin()

@Override
public void begin() {
  ……
  省略一些其他分支
  ……
  status = Status.WAITING_FOR_SIZE;
  if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
    onSizeReady(overrideWidth, overrideHeight);
  } else {
    target.getSize(this);
  }
  ……
  ……
}

begin() 方法很長,我們剔除了一些異常處理衩辟,直接看最核心的方法螟炫。

如果我給 Glide 設置了 override() 則直接調用 onSizeReady(overrideWidth, overrideHeight)

否則會調用 target.getSize(this) 讓 ImageView 計算自己的尺寸。

glideContext.buildImageViewTarget(view, transcodeClass) 創(chuàng)建一個 DrawableImageViewTarget

先看一些 DrawableImageViewTarget 類的繼承關系圖


gilde_01.png

這個類圖關系中 SizeDeterminer.getSize(SizeReadyCallback cb)就是計算 ImageView 尺寸

void getSize(SizeReadyCallback cb) {
  int currentWidth = getTargetWidth();
  int currentHeight = getTargetHeight();
  if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
    cb.onSizeReady(currentWidth, currentHeight);
    return;
  }

  // We want to notify callbacks in the order they were added and we only expect one or two
  // callbacks to be added a time, so a List is a reasonable choice.
  if (!cbs.contains(cb)) {
    cbs.add(cb);
  }
  if (layoutListener == null) {
    ViewTreeObserver observer = view.getViewTreeObserver();
    layoutListener = new SizeDeterminerLayoutListener(this);
    observer.addOnPreDrawListener(layoutListener);
  }
}

其中 getTargetWidth() 和 getTargetHeight()是用來計算 View 尺寸的艺晴,在 View 尺寸法傷改變的時候時候通過 SizeDeterminerLayoutListener 通知 SizeReadyCallback View 尺寸發(fā)生改變昼钻。

流程最終都會執(zhí)行 SingleRequest.onSizeReady(width, height) 方法

@Override
public void onSizeReady(int width, int height) {
  ……
  loadStatus = engine.load(
      glideContext,
      model,
      requestOptions.getSignature(),
      this.width,
      this.height,
      requestOptions.getResourceClass(),
      transcodeClass,
      priority,
      requestOptions.getDiskCacheStrategy(),
      requestOptions.getTransformations(),
      requestOptions.isTransformationRequired(),
      requestOptions.isScaleOnlyOrNoTransform(),
      requestOptions.getOptions(),
      requestOptions.isMemoryCacheable(),
      requestOptions.getUseUnlimitedSourceGeneratorsPool(),
      requestOptions.getUseAnimationPool(),
      requestOptions.getOnlyRetrieveFromCache(),
      this);
  ……
}

看到一個新的對象 Engine 從字面意思上可以猜測好像是用來加載圖片用的,查看 engine.load()

public <R> LoadStatus load(……) {
  ……
  如果資源已經(jīng)緩存封寞,直接調用 onResourceReady
  ……
  cb.onResourceReady(active, DataSource.MEMORY_CACHE);
  ……
  EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
      useUnlimitedSourceExecutorPool, useAnimationPool);
  DecodeJob<R> decodeJob = decodeJobFactory.build(……);
  jobs.put(key, engineJob);
  engineJob.addCallback(cb);
  engineJob.start(decodeJob);
  ……
  return new LoadStatus(cb, engineJob);
}

load() 方法很長换吧,上面只保留和核心的內容。

EngineJob 和 DecodeJob 是用來加載網(wǎng)絡資源的钥星,如果資源已經(jīng)存在沾瓦,不會重復加載。

先看下 cb.onResourceReady(active, DataSource.MEMORY_CACHE)

這里的 cb 就是 SingleRequest

public void onResourceReady(Resource<?> resource, DataSource dataSource) {
  ……
  ……
  onResourceReady((Resource<R>) resource, (R) received, dataSource);
}

private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
  ……
  try {
    if (……) {
      Transition<? super R> animation =
          animationFactory.build(dataSource, isFirstResource);
      target.onResourceReady(result, animation);
    }
  } finally {
    isCallingCallbacks = false;
  }

  notifyLoadSuccess();
}

流程會跳轉到 target.onResourceReady(result, animation) ,target 就是 DrawableImageViewTarget

@Override
public void onResourceReady(Z resource, @Nullable Transition<? super Z> transition) {
  if (transition == null || !transition.transition(resource, this)) {
    setResourceInternal(resource);
  } else {
    maybeUpdateAnimatable(resource);
  }
}

直接看 setResourceInternal(resource)

private void setResourceInternal(@Nullable Z resource) {
  maybeUpdateAnimatable(resource);
  setResource(resource);
}

@Override
protected void setResource(@Nullable Drawable resource) {
  view.setImageDrawable(resource);
}

以上終于看到 view.setImageDrawable(resource) 贯莺。

以上大致就是 ImgurGlide.with(getApplicationContext()).load(imageUrl).into(imageView) 的大致流程风喇,先總計如下。

Glide 從網(wǎng)絡下載圖片的流程更復雜缕探,后面重點講魂莫。

gilde_02.png

Glide 網(wǎng)絡加載圖片

Glide 網(wǎng)絡加載圖片比較負責,找了很久才找到做網(wǎng)絡請求的部分爹耗。這里單獨拉出來講耙考。
先回到 load() 方法。

public <R> LoadStatus load(……) {
  ……
  如果資源已經(jīng)緩存潭兽,直接調用 onResourceReady
  ……
  cb.onResourceReady(active, DataSource.MEMORY_CACHE);
  ……
  EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
      useUnlimitedSourceExecutorPool, useAnimationPool);
  DecodeJob<R> decodeJob = decodeJobFactory.build(……);
  jobs.put(key, engineJob);
  engineJob.addCallback(cb);
  engineJob.start(decodeJob);
  ……
  return new LoadStatus(cb, engineJob);
}

看一下 engineJob.start(decodeJob)

public void start(DecodeJob<R> decodeJob) {
  this.decodeJob = decodeJob;
  GlideExecutor executor = decodeJob.willDecodeFromCache()
      ? diskCacheExecutor
      : getActiveSourceExecutor();
  executor.execute(decodeJob);
}

這里看到了線程池操作 這里有兩個線程池倦始,分別對象兩種緩存方式。有興趣可以去看下這兩種線程池的配置

Engine 的初始化在 GlideBuilder.build(Context context)

    ……
    if (engine == null) {
      engine =
      new Engine(
      memoryCache,
      diskCacheFactory,
      diskCacheExecutor,
      sourceExecutor,
      GlideExecutor.newUnlimitedSourceExecutor(),
      GlideExecutor.newAnimationExecutor());
   }
   ……

那 DecodeJob 肯定是負責加載圖片的了山卦,先看 DecodeJob.run()

public void run() {
  ……
  try {
    if (isCancelled) {
      notifyFailed();
      return;
    }
    runWrapped();
  }
  ……
}

正常情況下會執(zhí)行

private void runWrapped() {
   switch (runReason) {
    case INITIALIZE:
      stage = getNextStage(Stage.INITIALIZE);
      currentGenerator = getNextGenerator();
      runGenerators();
      break;
    case SWITCH_TO_SOURCE_SERVICE:
      runGenerators();
      break;
    case DECODE_DATA:
      decodeFromRetrievedData();
      break;
    default:
      throw new IllegalStateException("Unrecognized run reason: " + runReason);
  }
}

這里可以看到有很多分析鞋邑,通過 Debug 可以知道這里走 INITIALIZE 拗踢。至于其他情況瞒渠,等著以后去研究吧。

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);
  }
}

因為我們傳入的是一個 Url 地址几蜻,會走 SOURCE 分支铸本,返回一個 SourceGenerator肮雨。

接著執(zhí)行 runGenerators()

 private void runGenerators() {
   currentThread = Thread.currentThread();
   startFetchTime = LogTime.getLogTime();
   boolean isStarted = false;
   while (!isCancelled && currentGenerator != null
       && !(isStarted = currentGenerator.startNext())) {
     stage = getNextStage(stage);
     currentGenerator = getNextGenerator();

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

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

這里比較重要的是 while 循環(huán)條件里面的 currentGenerator.startNext()),即 SourceGenerator

@Override
public boolean startNext() {
  ……
  while (!started && hasNextModelLoader()) {
    loadData = helper.getLoadData().get(loadDataListIndex++);
    if (……) {
      started = true;
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}

這又出現(xiàn)了新的對象 loadData 并且執(zhí)行 loadData.fetcher.loadData(……)

看起來特別想是加載網(wǎng)絡圖片操作箱玷,但是這部分代碼有點不好理解酷含。

loadData 是什么?
fetcher 又是什么汪茧?
搞不清池這兩個對象是什么,根本沒法繼續(xù)跟進了O薹2瘴邸!
尋找 loadData

根據(jù) while 那段代碼判斷 helper.getLoadData() 似乎返回了不止一個 loadData 弥虐,所以需要循環(huán)遍歷每一個 loadData 找到能執(zhí)行 loadData.fetcher.loadData(helper.getPriority(), this)的對象扩灯。

List<LoadData<?>> getLoadData() {
  if (!isLoadDataSet) {
    isLoadDataSet = true;
    loadData.clear();
    List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
    int size = modelLoaders.size();
    for (int i = 0; i < size; i++) {
      ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
      LoadData<?> current =
          modelLoader.buildLoadData(model, width, height, options);
      if (current != null) {
        loadData.add(current);
      }
    }
  }
  return loadData;
}

這里看出 DecodeHelper 對象遍歷 glideContext 的 Registry 對象,尋找匹配 model 的 ModelLoader

然后執(zhí)行 modelLoader.buildLoadData(model, width, height, options) 創(chuàng)建 LoadData 并添加到 List 返回霜瘪。

于是又引入兩個問題

ModelLoader 是啥玩意珠插?
modelLoader.buildLoadData() 做啥玩意?

啊~~~~~ 好頭疼S倍浴D沓拧!

繼續(xù)跟代碼

 public <Model> List<ModelLoader<Model, ?>> getModelLoaders(Model model) {
   List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
   if (result.isEmpty()) {
     throw new NoModelLoaderAvailableException(model);
   }
   return result;
 }
 
public synchronized <A> List<ModelLoader<A, ?>> getModelLoaders(A model) {
  List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
  int size = modelLoaders.size();
  List<ModelLoader<A, ?>> filteredLoaders = new ArrayList<>(size);
  for (int i = 0; i < size; i++) {
    ModelLoader<A, ?> loader = modelLoaders.get(i);
    if (loader.handles(model)) {
      filteredLoaders.add(loader);
    }
  }
  return filteredLoaders;
}

private <A> List<ModelLoader<A, ?>> getModelLoadersForClass(Class<A> modelClass) {
   List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
   if (loaders == null) {
     loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
     cache.put(modelClass, loaders);
   }
   return loaders;
 }

以上方法似乎還是看不出什么,在這里糾結很久顾患。最后發(fā)現(xiàn)在 Glide 構造方法中有以下代碼

Glide(……) {
  ……
  registry
      .append(ByteBuffer.class, new ByteBufferEncoder())
      .……
      這里省略很多  append
       ……
      .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
      .append(String.class, InputStream.class, new StringLoader.StreamFactory())
      ……
      這里省略很多  append
      ……
}

append() 方法

public <Model, Data> Registry append(Class<Model> modelClass, Class<Data> dataClass,
    ModelLoaderFactory<Model, Data> factory) {
  modelLoaderRegistry.append(modelClass, dataClass, factory);
  return this;
}

 public synchronized <Model, Data> void append(Class<Model> modelClass, Class<Data> dataClass,
     ModelLoaderFactory<Model, Data> factory) {
   multiModelLoaderFactory.append(modelClass, dataClass, factory);
   cache.clear();
 }

到這里我們終于看到了一點陽光

Glide 注冊了很多 modelLoader 
這些 modelLoader 就是加載各種資源用的

我們使用的字符串表示 URL 番捂,應該屬于 String 類型

.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())

處理 String 類型的有三個,但是我們是網(wǎng)絡下載圖片所以可以排除第三個江解。

每一個 ModelLoader 對象都有一個

public boolean handles(String url) {
    ……
}

DataUrlLoader 的 handles 只接受 "data:image" 開頭的字符设预,所以只剩下 StringLoader。

這里直接說出了結果犁河,具體每一步的調用可以按照上面思路鳖枕,配合單步調試了解詳情

然后看下 StreamFactory 和 StringLoader.buildLoadData()

 public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {

   @Override
   public ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
     return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
   }

   @Override
   public void teardown() {
     // Do nothing.
   }
 }

 @Override
public LoadData<Data> buildLoadData(String model, int width, int height,
    Options options) {
  Uri uri = parseUri(model);
  return uri == null ? null : uriLoader.buildLoadData(uri, width, height, options);
}

可以看到 StringLoader.buildLoadData() 又代理給了處理 <Uri.class, InputStream.class> 的 ModelLoader

然后在 Glide 的一堆 append 中找到對應的 UrlUriLoader.StreamFactory()

再次發(fā)現(xiàn),又代理給了 <GlideUrl.class, InputStream.class>,于是我們終于找到 HttpGlideUrlLoader 并且再沒有代理到其他 ModelLoader

Glide 的里面注冊很多 ModelLoader 只是一個包裹器桨螺,有具體操作的就幾種 ModelLoader 
HttpGlideUrlLoader 是其中一個宾符。

HttpGlideUrlLoader 的 buildLoadData 返回了我們要找的 LoadData

public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height,
    Options options) {
  ……
  return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
尋找 fetcher

找到 LoadData 的同時,我們也找到了 fetcher 即 HttpUrlFetcher

看名字我們就能知道彭谁,這個類肯定和 Http 有關吸奴。loadData.fetcher.loadData(helper.getPriority(), this) 的具體實現(xiàn)方法也跟著找到了

  @Override
  public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    final InputStream result;
    try {
      result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
          glideUrl.getHeaders());
    } 
    ……
    callback.onDataReady(result);
  }
  
  
  private InputStream loadDataWithRedirects(……) throws IOException {
  ……
  if (statusCode / 100 == 2) {
    return getStreamForSuccessfulRequest(urlConnection);
  } else if (statusCode / 100 == 3) {
    ……
    處理重定向
    ……
  } 
  ……
  異常處理
  ……
}

 private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
     throws IOException {
     ……
     stream = urlConnection.getInputStream();
   }
   return stream;
 }

以上這些代碼不做解釋了,就是發(fā)起網(wǎng)絡請求缠局,接收返回的數(shù)據(jù)流则奥。

loadData() 方法還有一句

callback.onDataReady(result);

這里的 callback 就是 SourceGenerator,所以看 SourceGenerator.onDataReady()

public void onDataReady(Object data) {
  DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
  if (……) {
    dataToCache = data;
    cb.reschedule();
  } else {
    cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
        loadData.fetcher.getDataSource(), originalKey);
  }
}

如果執(zhí)行成功了會執(zhí)行 cb.reschedule() 或者 cb.onDataFetcherReady() 這里的 cb 是 DecodeJob

public void reschedule() {
  runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
  callback.reschedule(this);
}

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

callback.reschedule(this) 其實就是 EngineJob..reschedule()

public void reschedule(DecodeJob<?> job) {
  // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
  // up.
  getActiveSourceExecutor().execute(job);
}  

用來再次執(zhí)行 DecodeJob 狭园,會改變 DecodeJob 的 runReason读处。這些任務調度比較復雜,經(jīng)過一些列 debug 得出結果如下

 經(jīng)過一些列的 reschedule 如果圖片下面成功會執(zhí)行 decodeFromRetrievedData()
 
 decodeFromRetrievedData 
 >> notifyEncodeAndRelease 
 >> notifyComplete 
 >> callback.onResourceReady

最終調用 EngineJob.onResourceReady()

public void onResourceReady(Resource<R> resource, DataSource dataSource) {
  this.resource = resource;
  this.dataSource = dataSource;
  MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}

public boolean handleMessage(Message message) {
     EngineJob<?> job = (EngineJob<?>) message.obj;
     switch (message.what) {
       case MSG_COMPLETE:
         job.handleResultOnMainThread();
         break;
       ……
     }
     return true;
   }
 }
 
void handleResultOnMainThread() {
  ……
  for (ResourceCallback cb : cbs) {
    if (!isInIgnoredCallbacks(cb)) {
      engineResource.acquire();
      cb.onResourceReady(engineResource, dataSource);
    }
  }
  ……
  engineResource.release();

  release(false /*isRemovedFromQueue*/);
}

handleResultOnMainThread 中的 cb.onResourceReady(engineResource, dataSource) 即通知資源獲取成功唱矛。這里的 cb 即 SingleRequest 和上一部分內容對接上了罚舱。

  public <R> LoadStatus load(……) {
    ……
    ……
    engineJob.addCallback(cb); // 傳入 SingleRequest
    engineJob.start(decodeJob);

    ……
  }

總結以上流程如下


gilde_03.png

備注

Glide 還有很多需要探究的知識點,這篇文章寫不下了绎谦。先寫那么多吧管闷。

參考資料

Gilde github 地址

Gilde 參考文檔

#2289 Why do you do?

Glide原理之Activity窃肠、Fragment生命周期監(jiān)聽(三)

Android圖片加載框架最全解析(二)包个,從源碼的角度理解Glide的執(zhí)行流程

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市冤留,隨后出現(xiàn)的幾起案子碧囊,更是在濱河造成了極大的恐慌,老刑警劉巖纤怒,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糯而,死亡現(xiàn)場離奇詭異,居然都是意外死亡泊窘,警方通過查閱死者的電腦和手機熄驼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門像寒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谜洽,你說我怎么就攤上這事萝映。” “怎么了阐虚?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵序臂,是天一觀的道長。 經(jīng)常有香客問我实束,道長奥秆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任咸灿,我火速辦了婚禮构订,結果婚禮上,老公的妹妹穿的比我還像新娘避矢。我一直安慰自己悼瘾,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布审胸。 她就那樣靜靜地躺著亥宿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砂沛。 梳的紋絲不亂的頭發(fā)上烫扼,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音碍庵,去河邊找鬼映企。 笑死,一個胖子當著我的面吹牛静浴,可吹牛的內容都是我干的堰氓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼苹享,長吁一口氣:“原來是場噩夢啊……” “哼双絮!你這毒婦竟也來了?” 一聲冷哼從身側響起富稻,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎白胀,沒想到半個月后椭赋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡或杠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年哪怔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡认境,死狀恐怖胚委,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情叉信,我是刑警寧澤亩冬,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站硼身,受9級特大地震影響硅急,放射性物質發(fā)生泄漏。R本人自食惡果不足惜佳遂,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一营袜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丑罪,春花似錦荚板、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祟峦,卻和暖如春罚斗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宅楞。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工针姿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厌衙。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓距淫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親婶希。 傳聞我的和親對象是個殘疾皇子榕暇,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容