常见用法
fun ImageView.loadPic(drawableId: Int) {
GlideApp.with(this)
.load(drawableId)
.centerCrop()
.into(this)
}
imageView.loadPic(R.drawable.ic_picture)
一般都把图片加载的方法封装起来,一来是为了使用方便,二来就算以后的实际加载方法变了(比如把Glide换成其他图片Picasso),也不会影响原来的代码。
源码解析
可以看实际调用的第一行代码传入了一个View,实际上with()方法有很多个重载方法,传入Context,Activity,Fragment都是可以的。这里我们从传入View的情况开始分析。
实际调用
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;
}
可以看到首先是调用Glide.with(view)获取一个RequestManager再强转为GlideRequests(GlideRequests继承于RequestManger)。而获取RequestManager则是通过RequestManagerRetriever的get(view)方法,接下来看下里面做了什么。
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);
}
-
首先判断当前是不是在子线程,如果是,则调用参数为ApplicationContext的get方法。因为Glide加载图片的时候是需要和生命周期绑定的,所以如果是在子线程加载则使用应用程序的生命周期。这种情况比较少,我们继续往下看。
-
然后就是判断view和view的Context是否为null,如果为null则抛出异常。
-
接着调用findActivity(view.getContext())来找到这个View是属于哪个Activity的,来看下具体的实现。
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;
}
}
首先判断这个Context是不是Activity,如果是的话则直接强转就行了。因为继承于Context的还有Service和Application,甚至还有这种。
- 如果调用findActivity获取到的Activity为null的话则还是调用参数为ApplicationContext的get方法。这种情况也比较少,我们继续往下看。
- 如果Activity是继承FragmentActivity的(AppCompatActivity继承于FragmentActivity),则通过findSupportFragment方法获取Fragment,如果获取的到则调用参数为Fragment的get方法,如果获取不到,则调用参数为Activity的get方法。那么来看下findSupportFragment的具体实现。
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清空,这个tempViewToSupportFragment是一个以View为key,Fragment为value的ArrayMap,从命名可以看出就是一个临时的map,所以每次根据View去找这个View所在的Fragment时都要清空。
接着它调用了findAllSupportFragmentsWithViews。
- 假设当前Activity有一个Fragment,叫做ParentFragment。这个ParentFragment里面还有一个Fragment叫做ChildFragment。那么一开始该方法传入的就是只包含ParentFragment的集合,然后遍历这个集合,如果里面的Fragment不为null,和Fragment.getView()也不为null则把Fragment.getView()作为key,fragment作为value放进tempViewToSupportFragment中。(tip:getView()返回的就是Fragment的onCreateView方法返回的那个View)
- 然后再递归调用该方法,因为Fragment里面可能还有Fragment,当没有时递归终止。
- 此时就是把ChildFragment.getView()和ChildFragment放进tempViewToSupportFragment,然后递归完成。
- 最终tempViewToSupportFragment的键值对为[ParentView,ParentFragment],[ChildView,ChildFragment]
这时再回到findSupportFragment()方法中,通过findViewById找到Activity的activityRoot(即是ContentView),然后把target(一开始的ImageView)赋值给current,接着开始找target所在的Fragment。这里假设在ChildFragment里面的View结构为<LinearLayout><ImageView/></LinearLayout>。
- 这里通过While循环,循环的条件是!current.equals(activityRoot),因为我们写的View都是在ContentView里面的,如果current如果等于contentView了则代表没找到,循环终止。
- 通过tempViewToSupportFragment.get(current)来获取Fragment,如果获取到则while循环终止,这里继续。
- 然后判断current的Parent是否继承于View,这里current的Parent是LinearLayout,所以进入if语句。把LinearLayout赋值给current,同时这个LinearLayout就是ChildView,接着进入下一轮循环。
- 这是由于current变成了ChildView,所以直接从tempViewToSupportFragment获取到ChildFragment,循环终止,把ChildFragment返回。
- 再回看下之前的代码
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
因为这里我们成功获取到了Fragment,所以最终会调用到参数为Fragment的get方法。
- 那么接下来看下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)方法已经检查过一遍了,但是使用Glide的时候除了get(view)方法也是可以直接使用get(fragment)方法的,所以这里还是要检查一遍线程。
- 接着又调用了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;
}
- 可以看到首先通过getSupportRequestManagerFragment()获取一个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;
}
首先通过findFragmentByTag()在FragmentManager中找是否之前已经有SupportRequestManagerFragment了。如果没有,就从pendingSupportRequestManagerFragments中获取,pendingSupportRequestManagerFragments是一个以FragmentManager为key,SupportRequestManagerFragment为value的HashMap。如果也没有,则创建一个SupportRequestManagerFragment,并且把参数Fragment作为它的父Fragment。
如果父Fragment是可见的,则调用SupportRequestManagerFragment的onStart生命周期。接着把刚创建的SupportRequestManagerFragment放进pendingSupportRequestManagerFragments,然后调用FragmentManager把这个SupportRequestManagerFragment添加进去,最后用handler发送消息把这个SupportRequestManagerFragment从pendingSupportRequestManagerFragments删除。
为什么放进了pendingSupportRequestManagerFragments又要马上删除呢?从命名可以看出里面是放待添加的Fragment,主要是为了FragmentManager还没把Fragment添加完成,而又调用了该方法。因为如果添加成功就可以调用findFragmentByTag获取到,如果没添加成功则可以通过pendingSupportRequestManagerFragments获取到,这样避免了在Fragment中添加多个SupportRequestManagerFragment。最后删掉是为了避免持有Fragment而导致不能释放引起内存泄漏。
-
回到supportFragmentGet()方法
首先获取RequestManager,新创建的SupportRequestManagerFragment是没的,所以接着通过RequestManagerFactory创建了一个,并传入给SupportRequestManagerFragment,然后就把requestManager返回。因为Glide.with()方法返回的就是一个requestManager,所以到这里流程就分析完了。 -
其实get(activity)的方法和get(fragment)差不多,只是如果是FragmentActivity则最终调用的还是supportFragmentGet()方法,只不过这里就是直接往FragmentActivity添加SupportRequestManagerFragment就行了。如果是Activity则调用的是fragmentGet()方法,添加的是RequestManagerFragment,区别在于这个Fragment是继承于Fragment还是support.v4包的Fragment。
总结
除了调用get(application)外,其他get()方法都是在Activity或者Fragment中添加一个Fragment,而这个Fragment里面有一个ActivityFragmentLifecycle变量,用来感应生命周期,从而使得图片的加载过程可以和生命周期相呼应。
所以如果是在Activity或者Fragment中加载,最好还是调用对应的get()方法,因为参数为View的还要去找一下这个View是在哪个Activity或者Fragment中。
网友评论