Glide源碼探究(一) Glide.with RequestManager.load RequestManager.into request的執行

蠻久之前囫圇吞棗地瞄過Glide部分源碼,最近由於某個契機又心血來潮比較系統的過了一遍它的源碼,發現它的蠻多設計還是比較有意思的。

首先Glide的使用十分簡單,只需要三行代碼就能完成圖片的下載、緩存和顯示:

Glide.with(this)
    .load(url)
    .into(img)

但爲了我們可以方便的使用,實際上每一行代碼的背後都幫我們做了不少髒活累活。

Glide.with

我們一行行來看,首先看Glide.with方法,它其實有一系列的重載:

public static RequestManager with(@NonNull Context context) {
  return getRetriever(context).get(context);
}

public static RequestManager with(@NonNull Activity activity) {
  return getRetriever(activity).get(activity);
}

public static RequestManager with(@NonNull FragmentActivity activity) {
  return getRetriever(activity).get(activity);
}

public static RequestManager with(@NonNull Fragment fragment) {
  return getRetriever(fragment.getContext()).get(fragment);
}

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

public static RequestManager with(@NonNull View view) {
  return getRetriever(view.getContext()).get(view);
}

內容大同小異,都是用getRetriever方法獲取RequestManagerRetriever,然後從裏面get出RequestManager。

Glide 單例模式實現

追蹤getRetriever方法,可以比較容易看出來實際上是從Glide單例裏面獲取RequestManagerRetriever,而且我們還能看到Glide使用的是單例模式的雙重校驗鎖方式:

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
        ...
    return Glide.get(context).getRequestManagerRetriever();
}

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

    return glide;
}

Glide生命週期綁定

glide其實已經幫我們對activty生命週期進行了綁定:在onStop的時候停止加載,在onStart的時候繼續加載,在onDestory清除任務和進行緩存的回收。它的原理是通過在Activity裏面添加一個不可見的fragment,在裏面監聽生命週期。

我們可以看到在RequestManagerRetriever.get方法最終會add一個SupportRequestManagerFragment到Activity裏面:

public RequestManager get(@NonNull FragmentActivity activity) {
    ...
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    ...
}

@NonNull
private RequestManager supportFragmentGet(Context context, FragmentManager fm, Fragment parentHint, boolean isParentVisible) {
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    ...
    return requestManager;
}

@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm, Fragment parentHint) {
    SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
        ...
        current = new SupportRequestManagerFragment();
        ...
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        ...
    }
    return current;
}

SupportRequestManagerFragment裏面有個lifecycle成員,我們可以向它註冊監聽,RequestManager也正是這麼幹的:

public class SupportRequestManagerFragment extends Fragment {
    ...
  private final ActivityFragmentLifecycle lifecycle;
  ...
  public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }
  ...
  @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();
  }
  ...
}
//RequestManager
RequestManager(
      Glide glide,
      Lifecycle lifecycle,
      RequestManagerTreeNode treeNode,
      RequestTracker requestTracker,
      ConnectivityMonitorFactory factory,
      Context context) {
    ...
    lifecycle.addListener(connectivityMonitor);
    ...
}

public synchronized void onStart() {
    resumeRequests();
    targetTracker.onStart();
}

@Override
public synchronized void onStop() {
    pauseRequests();
    targetTracker.onStop();
}

@Override
public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    ...
}

這種添加不可見Fragment監聽生命週期的技巧還是挺實用的。RxPermissions裏面也用了這種技巧去監聽onActivityResult回調

RequestManager.load

第二行的load方法,雖然只有一行但是實際上它做了兩件事情:

public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
}
  1. new了一個請求Drawable的RequestBuilder:
public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
}

public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
}
  1. 往builder裏設置了model:
public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    ...
    this.model = model;
    ...
}

沒錯這裏使用的是Builder模式。

RequestManager.into

into實際上也是幹了兩件事情,build了一個Request出來,然後runRequest執行它:

// RequestBuilder
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    ...
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
}

private <Y extends Target<TranscodeType>> Y into(
  @NonNull Y target,
  @Nullable RequestListener<TranscodeType> targetListener,
  BaseRequestOptions<?> options,
  Executor callbackExecutor) {
    ...
    Request request = buildRequest(target, targetListener, options, callbackExecutor);
    ...
  target.setRequest(request);
    requestManager.track(target, request);
    return target;
}

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

這裏其實還有個細節,我們展開講講。如果我們中途想取消對該view的圖片加載,可以用下面兩行代碼進行取消:

Glide.with(this)
    .clear(img)

這樣就要求我們要記錄view和request的關聯,可能有些同學會想到用map<View,Request>這樣的形式去保存,但是這樣的話需要我們額外做出一些手段去防止view的內存泄露(如弱引用等)。其實安卓裏比較常用的手段是將需要關聯的東西用setTag方法保存到view裏面:

// ViewTarget
public void setRequest(@Nullable Request request) {
    setTag(request);
}

private void setTag(@Nullable Object tag) {
    isTagUsedAtLeastOnce = true;
    view.setTag(tagId, tag);
}

於是clear的時候只需要用View.getTag把request再拿出來就好:

public void clear(@NonNull View view) {
    clear(new ClearTarget(view));
}

public void clear(@Nullable final Target<?> target) {
    if (target == null) {
      return;
    }
    untrackOrDelegate(target);
}

private void untrackOrDelegate(@NonNull Target<?> target) {
    boolean isOwnedByUs = untrack(target);
  Request request = target.getRequest();
  if (!isOwnedByUs && !glide.removeFromManagers(target) && request != null) {
      target.setRequest(null);
      request.clear();
  }
}

request的執行

runRequest的邏輯其實也比較簡單,如果沒有pause就begin request,如果不是就放到pendingRequests列表裏面等待後面再begin:

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

而request的begin方法的核心流程就是先獲取圖片的尺寸,然後使用engine.load去啓動真正的加載邏輯:

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

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,
          callbackExecutor);
    ...
}

到這裏其實一切都比較好理解,下一步就到了Engine這個Glide的核心模塊,Glide的實際加載和多級緩存都是由它去調度的,由於該模塊比較複雜,我們留到下一篇單獨來聊。

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