Glide4.0源码全解析(一),GlideAPP和.with()方法背后的故事
来源:互联网 发布:知乎不能修改不了答案 编辑:程序博客网 时间:2024/05/18 20:08
前言
在上一篇文章中我们体验了Glide-4.0的强大,以及更加简便的API,很多童鞋已经开始迁移了,那么接下来我们一起探索一下他的神奇之处:
首先我们来看一下4.0的基本用法:
GlideApp.with(this) .load(R.raw.large_giphy_logo) .into(giphyLogoView);
就这么简简单单的一句代码,其实Glide在背后帮我们做了成吨的事情。所以为了解开大家的好奇,一起来解析一下Glide-4.0源码。
准备
想要阅读源码那么久需要先下载源码,
GitHub地址:
https://github.com/bumptech/glide
我们这一篇是基于4.1.0讲解的,后续如果想查看这个版本的代码可以通过下面的链接:
https://github.com/bumptech/glide/tree/v4.1.1
剩下的应该不用说了吧,直接 git clone https://github.com/bumptech/glide.git
下载好源码之后我们可以看到里面有五个demo,我们可以运行一下看看效果,这里就不给大家挨着展示了。
到这里我们即将开启我们的源码之旅。
可能我们最大的疑惑就是glide为什么就用了简单的一句代码就可以实现图片的加载。 GlideApp.with(this).load(R.raw.large_giphy_logo).into(giphyLogoView);
首先,先说一下今天的解读思路:
Glide基本的加载步骤是三步那么我们就分别解读这三步with()
,load()
,into()
到底是如何实现的,以及GlideApp
是什么鬼?
今天这篇文章先讲述一下 GlideApp
和 with()
背后的故事。
GlideApp如何诞生?
1、 @GlideModule注解做了什么事情?
/** *在编译时,为AppGlideModules和LibraryGlideModules提供注入。 * *替换掉 AndroidManifest.xml 中value="GlideModule" 的 <meta-data /> 。 *这里需要注意后续需要用到<meta-data />这个标签,先记住此处 */@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface GlideModule { /** *此处返回的name就是你在使用时的class name。 * *eg:将GlideApp改为GlideAppX *那么通过注解生成的类就是GlideAppX *那么你使用时候就会是 GlideAppX.with(this) */ String glideName() default "GlideApp";}
注视已经详细介绍了这个类的作用,后面我们来看一下生成的GlideApp。
2、GlideApp能做啥?
GlideApp这个类事通过上面的注解获得到的,是不容许被修改的,是Glide在应用中的入口,做一些初始化,获取图片存储路径之类的,非常简单,注释也非常清楚,就不详细叙述了。
此处省略中间代码,
public final class GlideApp { private GlideApp() { } /** * @see Glide#getPhotoCacheDir(Context) */ @Nullable public static File getPhotoCacheDir(Context context) { return Glide.getPhotoCacheDir(context); } ... ... 省略 ... /** * @see Glide#with(View) */ public static GlideRequests with(View view) { return (GlideRequests) Glide.with(view); }}
那么到了这里我们已经知道了GlideAPP是如何诞生的以及GlideApp都有哪些方法。
接下来我们就详细看一下AppGlideModule做什么了,为啥要继承AppGlideModule?
3.为什么继承AppGlideModule?
我们先来看一下AppGlideModule的API
Class AppGlideModule
(1)isManifestParsingEnabled
我们先来看一下isManifestParsingEnabled()
用途:是否检测AndroidManifest里面的GlideModule
该方法,默认返回true。
但是如果我们通过上面的注解和继承AppGlideModule生成自己的module时,官方要求我们实现这个方法,返回并且false
,这样避免AndroidManifest加载两次。
下面是注释原文:
Implementations should return false after they and their dependencies have migrated to Glide's annotation processor.
我之前的文章对这个方法也有讲解可以去了解一下:
(2)applyOptions
在Glide被创建之前,对GlideBuilder进行设置。
方案非常多就不详细叙述了,如下图所示:
⚠️:这个方法只调用一次。
开始阅读—with()背后的故事!
1.GlideApp.with()方法干啥了?
/** * @see Glide#with(Context) */ public static GlideRequests with(Context context) { return (GlideRequests) Glide.with(context); } ... ... 入参不同省略 ... /** * @see Glide#with(View) */ public static GlideRequests with(View view) { return (GlideRequests) Glide.with(view); }
从上面的代码中可以看到我们使用GlideApp.with()
其实还是在使用Glide.with()
方法。
那么我们这里直接研究Glide.with()
方法:
2.Glide.with()方法究竟做啥了?
with()方法是Glide中的一组静态方法,有好几个重载方法,如下所示:
public static RequestManager with(Context context) { return getRetriever(context).get(context); } public static RequestManager with(Activity activity) { return getRetriever(activity).get(activity); } public static RequestManager with(FragmentActivity activity) { return getRetriever(activity).get(activity); } public static RequestManager with(android.app.Fragment fragment) { return getRetriever(fragment.getActivity()).get(fragment); } public static RequestManager with(Fragment fragment) { return getRetriever(fragment.getActivity()).get(fragment); } public static RequestManager with(View view) { return getRetriever(view.getContext()).get(view); }
可以看到,with()方法的重载种类非常多,如下:
with(android.app.Activity)
with(android.app.Fragment)
with(android.support.v4.app.Fragment)
with(android.support.v4.app.FragmentActivity)
with(android.view)
每一个with()方法重载的代码都非常简单,都是调用调用getRetriever(activity).get(activity)
,返回一个RequestManager对象。
那么我们先来看一下getRetriever(...).get(...)
做了什么事情?
3.getRetriever(…).get(…)做了什么事情?
1、getRetriever(…)做了什么事情?
如下图,这里是一个静态方法,返回RequestManagerRetriever,
这里官方已经帮我们做了判空处理,注释非常详细,这里就不再介绍了。
这里我们获得了RequestManagerRetriever,接下来看些.get(...)
方法做了什么?
2、getRetriever(…).get(…)做了什么事情?
第一步我们获得了RequestManagerRetriever,那么接下来我们详细看一下里面的get()方法。
RequestManagerRetriever是一个用静态方法的集合,这些方法包括新建RequestManager或者从Activity/Fragment检索出现有的。
API:RequestManagerRetriever
源代码如下:
public class RequestManagerRetriever implements Handler.Callback { public RequestManager get(Context context) { if (context == null) { throw new IllegalArgumentException("You cannot start a load on a null Context"); } else if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); } public RequestManager get(FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm, null /*parentHint*/); } } public RequestManager get(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); } } public RequestManager get(Activity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm, null /*parentHint*/); } } public RequestManager get(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. if (activity instanceof FragmentActivity) { Fragment fragment = findSupportFragment(view, (FragmentActivity) activity); if (fragment == null) { return get(activity); } return get(fragment); } // Standard Fragments. android.app.Fragment fragment = findFragment(view, activity); if (fragment == null) { return get(activity); } return get(fragment); } }
这里我们挨着对上面代码进行解析:在这里我们将入参分为两类,一类是Application,
一类是非Application
(1)当传入Application时:
如果在Glide.with()方法中传入的是一个Application对象,那么这里就会调用上面的get()方法重载,最后来获取一个RequestManager对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此Glide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。
public RequestManager get(Context context) { if (context == null) { throw new IllegalArgumentException("You cannot start a load on a null Context"); } else if (Util.isOnMainThread() && !(context instanceof Application)) { if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper) { return get(((ContextWrapper) context).getBaseContext()); } } return getApplicationManager(context); }
对ContextWrapper不熟悉的童鞋,可以参考这篇文章 Android源码装饰模式—ContextWrapper
(2)当传入的是非Application时:
⚠️:上图中可以看出来,如果在子线程,默认当application处理!!!
通过上图我们会发现不论传入Activity、FragmentActivity、Fragment最终都会调用图中红框中的方法,而这两个方法最终流程都是一致的就是那就是会向当前的Activity当中添加一个隐藏的Fragment。
下面是两个方法的具体代码: 对应的app包和v4包下的两种Fragment的情况。
接下来我们看fragmentGet()
和supportFragmentGet()
两个方法。
fragmentGet()方法
private RequestManager fragmentGet(Context context, android.app.FragmentManager fm, android.app.Fragment parentHint) { RequestManagerFragment current = getRequestManagerFragment(fm, parentHint); 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()); current.setRequestManager(requestManager); } return requestManager; }
supportFragmentGet()方法
private RequestManager supportFragmentGet(Context context, FragmentManager fm, Fragment parentHint) { SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint); 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()); current.setRequestManager(requestManager); } return requestManager; }
为什么使用Glide需要support-v4包??
⚠️:注意啦!
答:应该有童鞋遇到过下面的问题,引入Glide需要导入v4包,他在思考自己没有用到啊,其实是glide这里用到了,需要引入com.android.support:support-v4
。
是不是发现看了源码之后明白了为啥要添加v4包了!!!~~~~
那么这里为什么要添加一个隐藏的Fragment呢?
⚠️:注意啦!
答:因为Glide需要知道加载的生命周期。很简单的一个道理,如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,这时候Activity被用户关掉了,那么图片就应该取消加载,可是Glide并不知道Activity的生命周期,怎么办呢?
于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
RxPermissions也用到了这种技巧。
既然有了Application,为什么不用registerActivityLifecycleCallbacks而是用隐藏的Fragment?
⚠️:注意啦!
答:registerActivityLifecycleCallbacks是可以实现,并且我的小伙伴在自己的某些工程中也在使用,但是个人理解是这样的:
registerActivityLifecycleCallbacks监控所有的Activity生命周期,然而当你使用Glide加载图片时,并不是所有的Activity都会用到Glide加载图片(大多数情况),所以呢,使用registerActivityLifecycleCallbacks存在资源浪费的现象。不仅如此,你监控了所有的activity怎么和Glide想要监控的Activity关联到一块去,虽然可以实现,但是这个办法真心不实用,既然Glide给了我们这么完美的解决方案我们就要学以致用,以后尽力用到自己的工程中去。
GlideAPP()
和with()
方法到这里就结束了,以上为个人见解,有不同理解的小伙伴可以扫码左侧二维码,参与讨论哦!!!欢迎你来技术交流,无bb,不朋友!!
童鞋们期待着下一期的Glide源码分析吧!!!
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
- Glide4.0源码全解析(一),GlideAPP和.with()方法背后的故事
- Glide4.0源码全解析(三),into()方法背后的故事
- Glide4.0源码全解析(二),load()背后的故事
- Android 图片加载框架Glide4.0源码完全解析(一)
- Android 图片加载框架Glide4.0源码完全解析(二)
- 背后的故事之 - 快乐的Lambda表达式(一)
- Java main方法背后的故事?
- Glide4.0的基本用法
- Glide4.0+使用的坑
- Hello World程序背后的故事解密(一)—— 编译器的选项和C运行时库
- Swing和SWT背后的故事
- java swing和swt背后的故事
- LLVM和Clang背后的故事
- LLVM和Clang背后的故事
- StringBuffer和StringBuilder源码解析(一)--构造方法
- JUnit4执行cases背后的故事(1)---JUnitCore源码分析
- 网络蚂蚁背后的故事(转)
- 常见词汇背后的故事(1)
- 关于VLC播放rtsp流只能播放一帧的问题
- [1<2<3,3<2<1]
- static,final,abstract,接口
- Makefile 使用总结
- Android Studio 导入3方 jar出现的错误
- Glide4.0源码全解析(一),GlideAPP和.with()方法背后的故事
- oracle中的schema是什么?为什么要指定这个?
- hdoj 1533 && poj 2195 Going Home
- 第14期《成长之路》2017年9月刊
- Python logging 模块
- 【动态规划】HDU1081To The Max
- 9月6日笔记(mysql命令操作和frameset标签)
- Android资源文件strings实现特殊字符转义
- maven3.3.9报错