蠻久之前囫圇吞棗地瞄過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);
}
- 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);
}
- 往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的實際加載和多級緩存都是由它去調度的,由於該模塊比較複雜,我們留到下一篇單獨來聊。