常見(jiàn)用法
fun ImageView.loadPic(drawableId: Int) {
GlideApp.with(this)
.load(drawableId)
.centerCrop()
.into(this)
}
imageView.loadPic(R.drawable.ic_picture)
一般都把圖片加載的方法封裝起來(lái),一來(lái)是為了使用方便,二來(lái)就算以后的實(shí)際加載方法變了(比如把Glide換成其他圖片Picasso),也不會(huì)影響原來(lái)的代碼。
源碼解析
可以看實(shí)際調(diào)用的第一行代碼傳入了一個(gè)View,實(shí)際上with()方法有很多個(gè)重載方法,傳入Context,Activity,F(xiàn)ragment都是可以的。這里我們從傳入View的情況開(kāi)始分析。
實(shí)際調(diào)用
public static GlideRequests with(@NonNull View view) {
return (GlideRequests) Glide.with(view);
}
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
return Glide.get(context).getRequestManagerRetriever();
}
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
可以看到首先是調(diào)用Glide.with(view)獲取一個(gè)RequestManager再?gòu)?qiáng)轉(zhuǎn)為GlideRequests(GlideRequests繼承于RequestManger)。而獲取RequestManager則是通過(guò)RequestManagerRetriever的get(view)方法,接下來(lái)看下里面做了什么。
public RequestManager get(@NonNull View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(view.getContext(),
"Unable to obtain a request manager for a view without a Context");
Activity activity = findActivity(view.getContext());
// The view might be somewhere else, like a service.
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
// Support Fragments.
// Although the user might have non-support Fragments attached to FragmentActivity, searching
// for non-support Fragments is so expensive pre O and that should be rare enough that we
// prefer to just fall back to the Activity directly.
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
}
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
首先判斷當(dāng)前是不是在子線程,如果是,則調(diào)用參數(shù)為ApplicationContext的get方法。因?yàn)镚lide加載圖片的時(shí)候是需要和生命周期綁定的,所以如果是在子線程加載則使用應(yīng)用程序的生命周期。這種情況比較少,我們繼續(xù)往下看。
然后就是判斷view和view的Context是否為null,如果為null則拋出異常。
接著調(diào)用findActivity(view.getContext())來(lái)找到這個(gè)View是屬于哪個(gè)Activity的,來(lái)看下具體的實(shí)現(xiàn)。
private Activity findActivity(@NonNull Context context) {
if (context instanceof Activity) {
return (Activity) context;
} else if (context instanceof ContextWrapper) {
return findActivity(((ContextWrapper) context).getBaseContext());
} else {
return null;
}
}
首先判斷這個(gè)Context是不是Activity,如果是的話則直接強(qiáng)轉(zhuǎn)就行了。因?yàn)槔^承于Context的還有Service和Application,甚至還有這種。
- 如果調(diào)用findActivity獲取到的Activity為null的話則還是調(diào)用參數(shù)為ApplicationContext的get方法。這種情況也比較少,我們繼續(xù)往下看。
- 如果Activity是繼承FragmentActivity的(AppCompatActivity繼承于FragmentActivity),則通過(guò)findSupportFragment方法獲取Fragment,如果獲取的到則調(diào)用參數(shù)為Fragment的get方法,如果獲取不到,則調(diào)用參數(shù)為Activity的get方法。那么來(lái)看下findSupportFragment的具體實(shí)現(xiàn)。
private Fragment findSupportFragment(@NonNull View target, @NonNull FragmentActivity activity) {
tempViewToSupportFragment.clear();
findAllSupportFragmentsWithViews(
activity.getSupportFragmentManager().getFragments(), tempViewToSupportFragment);
Fragment result = null;
View activityRoot = activity.findViewById(android.R.id.content);
View current = target;
while (!current.equals(activityRoot)) {
result = tempViewToSupportFragment.get(current);
if (result != null) {
break;
}
if (current.getParent() instanceof View) {
current = (View) current.getParent();
} else {
break;
}
}
tempViewToSupportFragment.clear();
return result;
}
private static void findAllSupportFragmentsWithViews(
@Nullable Collection<Fragment> topLevelFragments,
@NonNull Map<View, Fragment> result) {
if (topLevelFragments == null) {
return;
}
for (Fragment fragment : topLevelFragments) {
// getFragment()s in the support FragmentManager may contain null values, see #1991.
if (fragment == null || fragment.getView() == null) {
continue;
}
result.put(fragment.getView(), fragment);
findAllSupportFragmentsWithViews(fragment.getChildFragmentManager().getFragments(), result);
}
首先把tempViewToSupportFragment清空,這個(gè)tempViewToSupportFragment是一個(gè)以View為key,F(xiàn)ragment為value的ArrayMap,從命名可以看出就是一個(gè)臨時(shí)的map,所以每次根據(jù)View去找這個(gè)View所在的Fragment時(shí)都要清空。
接著它調(diào)用了findAllSupportFragmentsWithViews。
- 假設(shè)當(dāng)前Activity有一個(gè)Fragment,叫做ParentFragment。這個(gè)ParentFragment里面還有一個(gè)Fragment叫做ChildFragment。那么一開(kāi)始該方法傳入的就是只包含ParentFragment的集合,然后遍歷這個(gè)集合,如果里面的Fragment不為null,和Fragment.getView()也不為null則把Fragment.getView()作為key,fragment作為value放進(jìn)tempViewToSupportFragment中。(tip:getView()返回的就是Fragment的onCreateView方法返回的那個(gè)View)
- 然后再遞歸調(diào)用該方法,因?yàn)镕ragment里面可能還有Fragment,當(dāng)沒(méi)有時(shí)遞歸終止。
- 此時(shí)就是把ChildFragment.getView()和ChildFragment放進(jìn)tempViewToSupportFragment,然后遞歸完成。
- 最終tempViewToSupportFragment的鍵值對(duì)為[ParentView,ParentFragment],[ChildView,ChildFragment]
這時(shí)再回到findSupportFragment()方法中,通過(guò)findViewById找到Activity的activityRoot(即是ContentView),然后把target(一開(kāi)始的ImageView)賦值給current,接著開(kāi)始找target所在的Fragment。這里假設(shè)在ChildFragment里面的View結(jié)構(gòu)為<LinearLayout><ImageView/></LinearLayout>。
- 這里通過(guò)While循環(huán),循環(huán)的條件是!current.equals(activityRoot),因?yàn)槲覀儗?xiě)的View都是在ContentView里面的,如果current如果等于contentView了則代表沒(méi)找到,循環(huán)終止。
- 通過(guò)tempViewToSupportFragment.get(current)來(lái)獲取Fragment,如果獲取到則while循環(huán)終止,這里繼續(xù)。
- 然后判斷current的Parent是否繼承于View,這里current的Parent是LinearLayout,所以進(jìn)入if語(yǔ)句。把LinearLayout賦值給current,同時(shí)這個(gè)LinearLayout就是ChildView,接著進(jìn)入下一輪循環(huán)。
- 這是由于current變成了ChildView,所以直接從tempViewToSupportFragment獲取到ChildFragment,循環(huán)終止,把ChildFragment返回。
- 再回看下之前的代碼
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
因?yàn)檫@里我們成功獲取到了Fragment,所以最終會(huì)調(diào)用到參數(shù)為Fragment的get方法。
- 那么接下來(lái)看下get(fragment)方法
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(fragment.getActivity(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
}
}
首先還是檢查是不是在子線程,雖然get(view)方法已經(jīng)檢查過(guò)一遍了,但是使用Glide的時(shí)候除了get(view)方法也是可以直接使用get(fragment)方法的,所以這里還是要檢查一遍線程。
- 接著又調(diào)用了supportFragmentGet()方法
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
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;
}
- 可以看到首先通過(guò)getSupportRequestManagerFragment()獲取一個(gè)SupportRequestManagerFragment,那么看下getSupportRequestManagerFragment()的代碼。
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
首先通過(guò)findFragmentByTag()在FragmentManager中找是否之前已經(jīng)有SupportRequestManagerFragment了。如果沒(méi)有,就從pendingSupportRequestManagerFragments中獲取,pendingSupportRequestManagerFragments是一個(gè)以FragmentManager為key,SupportRequestManagerFragment為value的HashMap。如果也沒(méi)有,則創(chuàng)建一個(gè)SupportRequestManagerFragment,并且把參數(shù)Fragment作為它的父Fragment。
如果父Fragment是可見(jiàn)的,則調(diào)用SupportRequestManagerFragment的onStart生命周期。接著把剛創(chuàng)建的SupportRequestManagerFragment放進(jìn)pendingSupportRequestManagerFragments,然后調(diào)用FragmentManager把這個(gè)SupportRequestManagerFragment添加進(jìn)去,最后用handler發(fā)送消息把這個(gè)SupportRequestManagerFragment從pendingSupportRequestManagerFragments刪除。
為什么放進(jìn)了pendingSupportRequestManagerFragments又要馬上刪除呢?從命名可以看出里面是放待添加的Fragment,主要是為了FragmentManager還沒(méi)把Fragment添加完成,而又調(diào)用了該方法。因?yàn)槿绻砑映晒涂梢哉{(diào)用findFragmentByTag獲取到,如果沒(méi)添加成功則可以通過(guò)pendingSupportRequestManagerFragments獲取到,這樣避免了在Fragment中添加多個(gè)SupportRequestManagerFragment。最后刪掉是為了避免持有Fragment而導(dǎo)致不能釋放引起內(nèi)存泄漏。
回到supportFragmentGet()方法
首先獲取RequestManager,新創(chuàng)建的SupportRequestManagerFragment是沒(méi)的,所以接著通過(guò)RequestManagerFactory創(chuàng)建了一個(gè),并傳入給SupportRequestManagerFragment,然后就把requestManager返回。因?yàn)镚lide.with()方法返回的就是一個(gè)requestManager,所以到這里流程就分析完了。其實(shí)get(activity)的方法和get(fragment)差不多,只是如果是FragmentActivity則最終調(diào)用的還是supportFragmentGet()方法,只不過(guò)這里就是直接往FragmentActivity添加SupportRequestManagerFragment就行了。如果是Activity則調(diào)用的是fragmentGet()方法,添加的是RequestManagerFragment,區(qū)別在于這個(gè)Fragment是繼承于Fragment還是support.v4包的Fragment。
總結(jié)
除了調(diào)用get(application)外,其他get()方法都是在Activity或者Fragment中添加一個(gè)Fragment,而這個(gè)Fragment里面有一個(gè)ActivityFragmentLifecycle變量,用來(lái)感應(yīng)生命周期,從而使得圖片的加載過(guò)程可以和生命周期相呼應(yīng)。
所以如果是在Activity或者Fragment中加載,最好還是調(diào)用對(duì)應(yīng)的get()方法,因?yàn)閰?shù)為View的還要去找一下這個(gè)View是在哪個(gè)Activity或者Fragment中。