本篇是 Glide 系列的最后一篇,主要讲一下 into 方法里面的逻辑。into 的逻辑也是最多最复杂的,可能需要反复阅读源码才能搞清楚。
Glide : https://github.com/bumptech/glide
version : v4.9.0
RequestBuilder
1 | @NonNull |
into 方法最后都会调用 into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor)
1 | private <Y extends Target<TranscodeType>> Y into( |
构造出请求 request 后,重点关注下 requestManager.track 方法,由 requestManager 来执行这个请求。
RequestManager
1 | synchronized void track(@NonNull Target<?> target, @NonNull Request request) { |
track 方法中调用了两个方法:
- TargetTracker.track() 方法会对当前 Target 的生命周期进行管理;
- RequestTracker.runRequest() 方法对当前请求进行管理;
RequestTracker
1 | public void runRequest(@NonNull Request request) { |
当 Glide 未处于暂停状态的时候,会直接使用 Request.begin() 方法开启请求。
SingleRequest
1 | @Override |
在 begin 方法中,重点关注下
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
这里会根据有没有强制设置图片宽度来分为两部分:
- 设置了,直接调用 onSizeReady
- 没设置,会去调用 target.getSize 。做的事情就是给 view 添加 addOnPreDrawListener 。这样的话,在绘制之前获取到了 view 的宽高,然后再回调 onSizeReady
所以,说到底,最后都是会调用 onSizeReady 的。
1 | @Override |
在 onSizeReady 中将状态更改为 Status.RUNNING ,并调用 engine 的 load() 方法。
Engine
1 | public <R> LoadStatus load( |
在 load 中,Glide 会先去取缓存,当缓存中不存在的时候就准备使用新创建一个任务来加载图片。我们这里就当作是第一次加载图片了,所以跟进 waitForExistingOrStartNewJob 方法中看看。
1 | private <R> LoadStatus waitForExistingOrStartNewJob( |
上面方法中创建了两个对象,一个是 DecodeJob、一个是 EngineJob。它们之间的关系是,EngineJob 内部维护了线程池,用来管理资源加载,已经当资源加载完毕的时候通知回调。 DecodeJob 继承了 Runnable,是线程池当中的一个任务。就像上面那样,我们通过调用 engineJob.start(decodeJob) 来开始执行图片加载的任务。
EngineJob
1 | public synchronized void start(DecodeJob<R> decodeJob) { |
在 run 方法中,当前任务没有被取消的话,会进入到 runWrapped() 方法。
1 | private void runWrapped() { |
stage 是 SOURCE ,所以获取到的就是 SourceGenerator 。接着就调用 runGenerators() 。
1 | private void runGenerators() { |
会先去执行 currentGenerator.startNext() 。所以接着就跳转到 SourceGenerator.startNext() 。
SourceGenerator
1 | @Override |
因为之前我们的想法是加载网络上的 url 图片,所以这里的 loadData 就对应着 HttpGlideUrlLoader
, fetcher 就是 HttpUrlFetcher 。
HttpUrlFetcher
1 | @Override |
loadDataWithRedirects 中会去调用 HttpURLConnection 加载网络上的图片数据。
加载完之后,会回调 onDataReady 方法。这个回调一直从 HttpUrlFetcher 中一直回调到 SourceGenerator 中。所以下面就来看看 SourceGenerator.onDataReady
SourceGenerator
1 | @Override |
在 onDataReady 中,会去判断如果 data 不为空并且磁盘缓存可以缓存的情况下,会调用 cb.reschedule(); 。这其实是调用了 DecodeJob 的 reschedule 方法。
DecodeJob
1 | @Override |
在这里,设置 runReason 为 RunReason.SWITCH_TO_SOURCE_SERVICE ,这很关键,在下面代码中会用到。然后再调用 callback.reschedule(this) 。其实就是调用了 EngineJob 的 reschedule 方法。
EngineJob
1 | @Override |
reschedule 方法摆明了就是让 DecodeJob 把 run 方法再跑一遍。之前说过,DecodeJob 的 run 方法里面大部分的逻辑其实是在 runWrapped 中的。
DecodeJob
1 | private void runWrapped() { |
这代码很熟悉,之前我们的流程到过这里。不同的是,之前的 runReason 是 INITIALIZE 。而现在的 runReason 是 SWITCH_TO_SOURCE_SERVICE 。
接着 SWITCH_TO_SOURCE_SERVICE 的逻辑是直接调用 runGenerators 方法。
1 | private void runGenerators() { |
这里的 currentGenerator 还是之前的 SourceGenerator ,所以还是调用 SourceGenerator.startNext 。
SourceGenerator
1 | @Override |
这次调用 SourceGenerator.startNext 其实是建立磁盘缓存,直接来看 cacheData 方法。
1 | private void cacheData(Object dataToCache) { |
这里的主要逻辑是构建一个用于将数据缓存到磁盘上面的 DataCacheGenerator。DataCacheGenerator 的流程基本与 SourceGenerator 一致,也就是根据资源文件的类型找到 ModelLoader,然后使用 DataFetcher 加载缓存的资源。与之前不同的是,这次是用 DataFecher 来加载 File 类型的资源。也就是说,当我们从网络中拿到了数据之后 Glide 会先将其缓存到磁盘上面,然后再从磁盘上面读取图片并将其显示到控件上面。所以,当从网络打开了输入流之后 SourceGenerator 的任务基本结束了,而后的显示的任务都由 DataCacheGenerator 来完成。
再回过头来看看 SourceGenerator.startNext 方法,在 cacheData 后面会对 sourceCacheGenerator 进行判断。由于上面已经把 sourceCacheGenerator 对象 new 出来了。所以接着就直接走 DataCacheGenerator 的 startNext 方法了。所以上面这段话就很好理解了。
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
那么,这里我们对 DataCacheGenerator 的逻辑就省略了。DataCacheGenerator 中的流程最终会走到
ByteBufferFetcher 。
之前的 SourceGenerator 对应着 HttpUrlFetcher ,而 DataCacheGenerator 对应着 ByteBufferFetcher 。
ByteBufferFetcher
1 | @Override |
磁盘缓存好之后,再从文件中读取图片的字节流。回调给 DataCacheGenerator
DataCacheGenerator
1 | @Override |
DataCacheGenerator 会回调 DecodeJob 的 onDataFetcherReady 方法。
DecodeJob
1 | @Override |
这里我们可以看下,当 resource 最终获取到后,是通过 notifyEncodeAndRelease 来通知任务完成的。这在后面的代码解析中会讲到。
现在,我们就来看看关键的逻辑,接着调用 decodeFromData 。
1 | private <Data> Resource<R> decodeFromData( |
调用 decodeFromFetcher 方法。
1 | @SuppressWarnings("unchecked") |
调用了 runLoadPath 。
1 | private <Data, ResourceType> Resource<R> runLoadPath( |
然后会调用 DecodePath 的 decode 方法。
DecodePath
1 | public Resource<Transcode> decode( |
在 decode 中做了三件事:
- decodeResource 将原始数据转换成我们原始图片的过程;
- callback.onResourceDecoded 是当得到了原始图片之后对图片继续处理过程;
- transcoder.transcode 会使用 BitmapDrawableTranscoder 包装一层,即对 Drawable 进行延迟初始化处理。
那么我们接着跟进 decodeResource 方法。
1 | @NonNull |
主要逻辑在 decodeResourceWithList 中。
1 | @NonNull |
ResourceDecoder 具有多个实现类,比如 BitmapDrawableDecoder、ByteBufferBitmapDecoder等。从名字也可以看出来是用来将一个类型转换成另一个类型的。
在这里会使用 ByteBufferBitmapDecoder 来将 ByteBuffer 专成 Bitmap 。
ByteBufferBitmapDecoder
1 | @Override |
它最终会在 Downsampler 的 decodeStream() 方法中调用 BitmapFactory 的 decodeStream() 方法来从输入流中得到 Bitmap。
在 Downsampler 内部还会维持一个 BitmapPool ,用来复用 Bitmap 。有兴趣的同学可以看下这一块的代码,这里就不过多展示了。
接下来,就来看看上面 callback.onResourceDecoded 的逻辑。callback.onResourceDecoded 会调用 DecodeJob.onResourceDecoded 方法。
DecodeJob
1 | @Synthetic |
主要的逻辑是根据我们设置的参数进行变化。也就是说,如果我们使用了 centerCrop 等参数,那么这里将会对其进行处理。这里的 Transformation 是一个接口,它的一系列的实现都是对应于 scaleType 等参数的。
到了这里, Glide 所有加载图片、处理图片的逻辑都讲完了。剩下的,就是将图片显示到 ImageView 上面了。
我们再回过头来看之前讲到 DecodeJob.notifyEncodeAndRelease 方法
DecodeJob
1 | private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) { |
来看 notifyComplete(result, dataSource); 方法
1 | private void notifyComplete(Resource<R> resource, DataSource dataSource) { |
回调 EngineJob 的 onResourceReady 方法。
EngineJob
1 | @Override |
关键在 notifyCallbacksOfResult 中。
1 | @Synthetic |
在代码的最后,
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
可以看到这里会执行 CallResourceReady 。
CallResourceReady
1 | private class CallResourceReady implements Runnable { |
最后还是用回调调用了 SingleRequest 的 onResourceReady 方法。
SingleRequest
1 | @Override |
最后调用 onResourceReady((Resource
1 | private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) { |
发现上面的代码调用了 target.onResourceReady(result, animation);
这里的 target 一般都是 ImageViewTarget 。ImageViewTarget 是个抽象类。
ImageViewTarget
1 | @Override |
setResource(resource) 是抽象方法,我们到子类中看看。我们挑 DrawableImageViewTarget 来看看吧。
DrawableImageViewTarget
1 | @Override |
终于,我们看到了 ImageView.setImageDrawable 来显示图片了,不容易啊。
总结
真的没想到,短短的一句 Glide.with(context).load(“http://www.xxxx.com/xx.jpg").into(imageView) 代码内部竟然隐藏着如此庞大的逻辑。相信你看完这一系列的文章,对 Glide 会刮目相看吧。
当然,本系列还有很多 Glide 中没讲到的知识点,比如缓存具体的应用等,如果想了解的同学可以自行去阅读下源码。