Glide源码学习笔记一:with方法做了什么?
文章目录
- Glide源码学习笔记一:with方法做了什么?
- 前言
- 一、Glide的结构?
- 二、Glide加载三部曲之Glide.with
- Glide.with(@NonNull Context context) =>RequestManager
- Glide.getRetriever(@Nullable Context context)=>RequestManagerRetriever
- Glide.get(context)=>Glide
- RequestManagerRetriever.get(Context)=>RequestManager
- RequestManagerRetriever.get(FragmentActivity)=>RequestManager
- RequestManager.supportFragmentGet(Context ,FragmentManager,Fragment ,boolean)=>RequestManager
- 总结
前言
在面试过程中,经常会问到Glide原理相关问题,问的人多了,就说明代码风格或者设计风格都有值得学习的地方。于是想记录自己在探索Glide源码遇到的问题以及想法,写下了这几篇文章。所有源码的版本是4.13.1
一、Glide的结构?
Glide的用法大家都很熟悉,最简单的如下三个步骤即可加载图片:
Glide.with(context).load(url).into(imageView);
Glide.with(Context) | .apple(BaseRequestOptions). | .into(Object) |
---|---|---|
该方法返回的是一个RequestManager对象,这一步主要是做一个生命周期的绑定,加载图片的动作和生命周期保持一致 | 该方法返回的是一个RequestBuilder,在这里需要确定图片尺寸,图片类型,是否使用占位图等等一系列加载参数。使得加载图片更加灵活的满足开发需要。 | 图片加载的入口,在前两步只是最基本的操作,加载,解码,网络请求,缓存等等核心问题都是从该方法开始,可以说是Glide最核心的方法。 |
二、Glide加载三部曲之Glide.with
Glide.with有如下六种重载:
Glide.with(@NonNull Context context) =>RequestManager
跟进一下,会发现有如下几种重载:
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getContext()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
这个时候有一个疑问,为什么要区分页面(上下文)
呢?结合我们在使用过程中的经验,有时候是在Fragment中加载,有时候在Activity加载,还有时候在自定义的View中加载,区分场景处理有什么好处呢?带着问题继续分析:
先看getRetriever
方法,
Glide.getRetriever(@Nullable Context context)=>RequestManagerRetriever
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
该方法首先做了一个上下文的空指针校验,返回值由两个方法构成,先看第一个Glide.get(context):
Glide.get(context)=>Glide
@NonNull
// Double checked locking is safe here.
@SuppressWarnings("GuardedBy")
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
在这里做了一个double check,返回值变量glide
就是Glide的实例,这个方法是不是和我们写单例的double check很像?
这个初始化的详细方法,在下一篇文章中来学习。回到之前的RequestManagerRetriever .get()方法,和Glide.with()的重载方法一一对应:
RequestManagerRetriever.get(Context)=>RequestManager
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {//空指针判断
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {//线程判断和Application判断
if (context instanceof FragmentActivity) {//上下文类型判断
return get((FragmentActivity) context);
} else if (context instanceof Activity) {//上下文类型判断
return get((Activity) context);
} else if (context instanceof ContextWrapper
// Only unwrap a ContextWrapper if the baseContext has a non-null application context.
// Context#createPackageContext may return a Context without an Application instance,
// in which case a ContextWrapper may be used to attach one.
//翻译:只有当baseContext有一个不为空的application context才可以打开ConextWrapper,
//Context的createPackageContext方法可能在没有Application实例的情况下返回一个Context,
//在这种情况下,一个ContextWrapper可能被建立和Glide的关系
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {//上下文类型判断
return get(((ContextWrapper) context).getBaseContext());
}
}
//兜底的RequestManager
return getApplicationManager(context);
}
从代码中可以发现,如果传入的是Context,他可能来自当前页面
(Activity/FragmentActivity),在这里做一个类型区分,在调用相应的重载方法。接着往下看:
RequestManagerRetriever.get(FragmentActivity)=>RequestManager
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
传入的参数是FragmentActivity,首先做一个判断,如果在后台,更改为ApplicationContext。这个时候我们好像知道了什么,是不是RequestManager持有的上下文,需要和当前加载图片的场景保持一致?接着看else里面的方法。首先判断当前的FragmentActivity有没有被销毁,如果销毁了就抛出一个异常。frameWaiter这个类不是很常见,从字面意思解读框架服务员
?为什么activity要在这里注册他自己
?我们看代码很容易找到是在RequestManagerRetriever构造函数中new出来的,具体有什么用,我们先记下来,打一个todo,看完真个流程的的Glide代码应该就可以知道了。接下来获取FragmentManager,这个类我们经常会在ViewPager中切换,获取Fragment用到,再看返回方法:
RequestManager.supportFragmentGet(Context ,FragmentManager,Fragment ,boolean)=>RequestManager
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,//在这里传递的是null
boolean isParentVisible) {
//获取一个自定义的Fragment
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
//获取与次fragment绑定的requestManager
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
//request为控,重新使用工厂方法构造,绑定生命周期和fragment
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// This is a bit of hack, we're going to start the RequestManager, but not the
// corresponding Lifecycle. It's safe to start the RequestManager, but starting the
// Lifecycle might trigger memory leaks. See b/154405040
//这有点像黑客,我们将启动RequestManager,但是不符合生命周期,启动RequestManager安全,但是启动生命周期
//可能引起内存泄漏
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager(requestManager);
}
return requestManager;
}
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
// If we have a pending Fragment, we need to continue to use the pending Fragment. Otherwise
// there's a race where an old Fragment could be added and retrieved here before our logic to
// add our pending Fragment notices. That can then result in both the pending Fragmeng and the
// old Fragment having requests running for them, which is impossible to safely unwind.
//如果准备好的Fragment就用,没有就创建一个
SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//发送了一条handler消息
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
上述代码我们可以知道,一个RequestManager是和一个SupportRequestManagerFragment互相绑定的关系,在当前传入的Fragment中添加一个SupportRequestManagerFragment,实现了生命周期的绑定。之前那么多重载方法,也是为了找到一个页面进行添加Fragment。
总结
Glide.with()方法主要做的事情就是添加一个Fragment在需要加载图片的界面,区分了多种页面类型,例如:Activity,FragmentActivity,Fragment,View,在构造Fragment的时候,会先从一个Map里面根据FragmentManager作为Key来取,减少了创建次数。在持有了加载界面的生命周期之后,就可以避免在页面销毁后还在加载图片的情况。遗留了一个问题:frameWaiter
的作用是什么?