“抽丝剥茧,点到即止”
阅读源码应认准一个功能点,然后去分析这个功能是如何实现的。但只要去追寻主体的实现逻辑即可,不要试图搞懂每一行代码。
Glide源码就定一个以下目标:
1 | Glide.with(this).load(url).into(imageView); |
with()
with()方法是 Glide 类中的一组静态方法,它有好几个方法重载,我们来看一下 Glide 类中所
有 with()方法的方法重载:
1 | public class Glide { |
可看到,with()的重载种类非常多。每个with()方法重载的代码都非常简单,都是先调用RequestManagerRetriever
的静态get()
得到对象,这个静态get()
方法就是一个单例实现。再调用RequestManagerRetriever
的实例的get()
方法,去获取RequestManager
对象
1 | public class RequestManagerRetriever implements Handler.Callback{ |
上述代码看起来逻辑优点复杂,其实分两种情况:传入Application类型的参数,传入非Application类型的参数。
若Glide.with()
传入Application
对象,那么这里会调用带有Context
参数的get()
方法重载,会调用getApplicationManager()
获取RequestManager
对象。另外,因为Application
对象的生命周期即应用程序的生命周期,所以 Glide 不需要做特殊处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。
若Glide.with()
传入非Application
参数情况,不管传入的是Activity
、FragmentAcitivity
、v4
包下的Fragment
、还是app
包下的Fragment
,最终都是向当前Activity
中添加一个隐藏的Fragment
。如此设计的原因是Glide
需要知道加载的生命周期,若图片还没加载出来activity
就结束了那么就不用加载图片了。当时因为Glide
无法知道Activity
的生命周期,所以借助添加隐藏Fragment
的小技巧,Fragment
的生命周期和Activity
的同步,可以监听到Activity
的销毁,只有Glide
就可以捕获这个事件并停止图片加载了。
另外,如果在非主线程当中使用Glide
,那么不管传入的是Activity
还是Fragment
,都会被强制当成Application
来处理。
总体来说,第一个with()
方法的源码就是为了得到一个RequestManager
对象,然后Glide
会根据传入with()
的参数来确定图片加载的生命周期。
load()
由于with()
返回的是RequestManager
对象,那么容易想到load()
是在RequestManager
类中。Glide
支持片URL
字符串、图片本地路径等等加载形式,我们选其中一个加载图片URL
字符串的load()
来研究。
RequestManager
类简化代码:
1 | public class RequestManager implements LifecycleListener{ |
RequestManager
类的代码是非常多的,但是经过我这样简化之后,看上去就比较清爽了。
在我们只探究加载图片 URL 字符串这一个 load()方法的情况下,那么比较重要的方法就只剩下上述代码中的这三个方法。
那么我们先来看 load()
方法,这个方法中的逻辑是非常简单的,只有一行代码。
了 fromString()方法,再调用 load()方法,然后把传入的图片 URL 地址传进去。而 fromString()
方法也极为简单,就是调用了 loadGeneric()方法,并且指定参数为 String.class,因为 load()
方法传入的是一个字符串参数。那么看上去,好像主要的工作都是在 loadGeneric()方法中进
行的了。 其实 loadGeneric()
方法也没几行代码,这里分别调用了 Glide.buildStreamModelLoader()
方法和 Glide.buildFileDescriptorModelLoader()
方法来获得 ModelLoader
对象。ModelLoader
对象是用于加载图片的,而我们给 load()方法传入不同类型的参数,这里也会得到不同的ModelLoader
对象。由于我们刚才传入的参 数是 String.class
,因此最终得到的是 StreamStringLoader
对象,它是实现了 ModelLoader
接口的。
最后我们可以看到,loadGeneric()
方法是要返回一个 DrawableTypeRequest
对象的,因此在loadGeneric()
方法的最后又去 new 了一个 DrawableTypeRequest
对象,然后把刚才获得的 ModelLoader
对象,还有一大堆杂七杂八的东西都传了进去。具体每个参数的含义和作用就不解释了,我们只看主线流程。
DrawableTypeRequest
的作用是什么呢?我们来看下它的源码
1 | public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions { |
这个类中的代码本身就不多,我只是稍微做了一点简化。可以看到,最主要的就是它提供了 asBitmap()
和 asGif()
这两个方法。这两个方法我们在上一篇文章当中都是学过的,分别是用于强制指定加载静态图片和动态图片。而从源码中可以看出,它们分别又创建了一个BitmapTypeRequest
和 GifTypeRequest
,如果没有进行强制指定的话,那默认就是使用DrawableTypeRequest
。
好的,那么我们再回到 RequestManager
的 load()方法中。刚才已经分析过了,fromString()
方法会返回一个 DrawableTypeRequest
对象,接下来会调用这个对象的 load()方法,把图片的 URL 地址传进去。但是我们刚才看到了,DrawableTypeRequest
中并没有 load()方法,那么很容易就能猜想到,load()方法是在父类当中的。
DrawableTypeRequest
的父类是 DrawableRequestBuilder
,我们来看下这个类的源码:
1 | public class DrawableRequestBuilder<ModelType> |
DrawableRequestBuilder
中有很多个方法,这些方法其实就是 Glide 绝大多数的 API
了。里面有 不 少 我 们 在 上 篇 文 章 中 已 经 用 过 了 , 比 如 说 placeholder()
方 法 、 error()
方 法 、diskCacheStrategy()
方法、override()
方法等。当然还有很多暂时还没用到的 API
,我们会在后面的文章当中学习。
到 这 里 , 第 二 步 load()
方 法 也 就 分 析 结 束 了 。 为 什 么 呢 ? 因 为 你 会 发 现 DrawableRequestBuilder
类中有一个 into()方法(上述代码第 220 行),也就是说,最终 load() 方法返回的其实就是一个 DrawableTypeRequest
对象。那么接下来我们就要进行第三步了, 分析 into()方法中的逻辑。
into()
如果说前面两步都是在准备开胃小菜的话,那么现在终于要进入主菜了,因为 into()
方法也是整个 Glide 图片加载流程中逻辑最复杂的地方。
不过从刚才的代码来看,into()方法中并没有任何逻辑,只有一句 super.into(view)
。那么很显然,into()
方法的具体逻辑都是在 DrawableRequestBuilder
的父类当中了。 DrawableRequestBuilder
的 父 类 是 GenericRequestBuilder
, 我 们 来 看 一 下 GenericRequestBuilder
类中的 into()
方法,如下所示:
1 | public Target<TranscodeType> into(ImageView view) { |
这里前面一大堆的判断逻辑我们都可以先不用管,等到后面文章讲 transform
的时候会再进行 解 释 , 现 在 我 们 只 需 要 关 注 最 后 一 行 代 码 。 最 后 一 行 代 码 先 是调用了glide.buildImageViewTarget()
方法,这个方法会构建出一个 Target 对象,Target 对象则是用来最终展示图片用的,如果我们跟进去的话会看到如下代码:
1 | <R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) { |
这里其实又是调用了 ImageViewTargetFactory
的 buildTarget()
方法,我们继续跟进去,代码如下所示:
1 | public class ImageViewTargetFactory { |
可以看到,在 buildTarget()
方法中会根据传入的 class
参数来构建不同的 Target
对象。那如果你要分析这个 class
参数是从哪儿传过来的,这可有得你分析了,简单起见我直接帮大家梳理清楚。这个 class
参数其实基本上只有两种情况,如果你在使用 Glide
加载图片的时候调用了 asBitmap()
方法,那么这里就会构建出 BitmapImageViewTarget
对象,否则的话构建的都是 GlideDrawableImageViewTarget
对象。至于上述代码中的 DrawableImageViewTarget
对象,这个通常都是用不到的,我们可以暂时不用管它。也 就 是 说 , 通 过 glide.buildImageViewTarget()
方 法 , 我 们 构 建 出 了 一 个GlideDrawableImageViewTarget
对象。那现在回到刚才 into()
方法的最后一行,可以看到,这里又将这个参数传入到了 GenericRequestBuilder
另一个接收 Target
对象的 into()
方法当中了。
我们来看一下这个 into()
方法的源码:
1 | public <Y extends Target<TranscodeType>> Y into(Y target) { |
这里我们还是只抓核心代码,其实只有两行是最关键的,第 15 行调用 buildRequest()
方法构建出了一个 Request
对象,还有第 18 行来执行这个 Request
。 Request
是用来发出加载图片请求的,它是 Glide
中非常关键的一个组件。
我们先来看buildRequest()
方法是如何构建 Request
对象的:
1 | private Request buildRequest(Target<TranscodeType> target) { |
可 以 看 到 , buildRequest()
方 法 的 内 部 其 实 又 调 用 了 buildRequestRecursive()
方 法 , 而buildRequestRecursive()
方法中的代码虽然有点长,但是其中 90%的代码都是在处理缩略图的。如果我们只追主线流程的话,那么只需要看第 47 行代码就可以了。这里调用了obtainRequest()
方 法 来 获 取 一 个 Request
对 象 , 而 obtainRequest()
方 法 中 又 去 调 用 了GenericRequest
的 obtain()
方法。注意这个 obtain()
方法需要传入非常多的参数,而其中很多的参数我们都是比较熟悉的,像什么 placeholderId
、errorPlaceholder
、diskCacheStrategy
等等。因此,我们就有理由猜测,刚才在 load()
方法中调用的所有 API
,其实都是在这里组装到 Request
对象当中的。
那么我们进入到这个 GenericRequest
的 obtain()
方法瞧一瞧:
1 | public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback, ResourceCallback { |
可以看到,这里在第 33 行去 new 了一个 GenericRequest
对象,并在最后一行返回,也就是说,obtain()
方法实际上获得的就是一个 GenericRequest
对象。另外这里又在第 35 行调用了GenericRequest
的 init()
,里面主要就是一些赋值的代码,将传入的这些参数赋值到GenericRequest
的成员变量当中,我们就不再跟进去看了。
好,那现在解决了构建 Request
对象的问题,接下来我们看一下这个 Request
对象又是怎么 执行的。回到刚才的 into()
方法,你会发现在第 18 行调用了 requestTracker.runRequest()
方法 来去执行这个 Request
,那么我们跟进去瞧一瞧,如下所示:
1 | /** |
这里有一个简单的逻辑判断,就是先判断 Glide 当前是不是处理暂停状态,如果不是暂停状态就调用 Request 的 begin()
方法来执行 Request
,否则的话就先将 Request 添加到待执行队列里面,等暂停状态解除了之后再执行。
暂停请求的功能仍然不是这篇文章所关心的,这里就直接忽略了,我们重点来看这个 begin()
方法。由于当前的 Request
对象是一个 GenericRequest
,因此这里就需要看 GenericRequest
中的 begin()
方法了,如下所示: