在《Glide工作总体执行流程概述》一篇博文中简单分析了glide的工作流程,简而言之就是Glide先构建RequestManager对象,然后RequestManager对象构建ReqeustBuilder对象,再由RequsetBuilder对象创建一个Requset对像,最后将Request交给RequsetMangaer管理,然后RequestManager通知request调用begin发起请求将生成的图片资源交给具体的Target的过程。只不过上篇文章只是简单的做了梳理工作,并没有对详细的说明,本篇就承接上文来继续分析。所以建议读此篇博文的童鞋大致瞅一眼《Glide工作执行流程概述》

本篇还是以SingleRequest为例来说明,在资源还没有加载好的时候,SingleRequest的begin方法会调用一个onSizeReady方法:

public void begin() {//省略部分代码status = Status.WAITING_FOR_SIZE;if (Util.isValidDimensions(overrideWidth, overrideHeight)) {onSizeReady(overrideWidth, overrideHeight);} //省略部分代码}

进入onSizeReady内部翻看一翻,仍然剔除暂时与本文无关的代码:

private Engine engine;
public void onSizeReady(int width, int height) {//省略部分代码status = Status.RUNNING;loadStatus = engine.load(glideContext,model,//url//省略一大堆参数);}

也就是说onSizeReady方法内部也就是主要时调用来Engine对象的load方法,那么这个engine对象是什么鬼呢?先看看这个对象是时候初始化的,该对象是在初始化Glide对象的时候进行来初始化,具体的可在GlideBuilder的build方法找到:

 public Glide build(Context context) {//省略本分代码//如果客户端没有配置自己的Engineif (engine == null) {engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor,GlideExecutor.newUnlimitedSourceExecutor());}return new Glide(context,engine,//省略一大堆参数);}

从Engine类的构造起所需要的参数来看,Engine负责内存缓存,磁盘缓存等等管理工作,这些内存和磁盘缓存等相关的类可以通过Builder模式让客户端自己构建自己的缓存逻辑,这也是Builder模式的强大之处,该模式也算是常用的设计模式之一,其设计理念还是很值得借鉴的,到此为止Engine的初始化已经创建完毕。下面继续沿着SingleRequest的流程分析engine.load方法:

//返回一个LoadStatus对象public <R> LoadStatus load(//一大堆参数) {//1、生成一个EngineKey对象EngineKey key = keyFactory.buildKey(model,//urlsignature, width, height, transformations,resourceClass,transcodeClass, //Drawable.classoptions);//2、从缓存加载图片资源EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);if (cached != null) {cb.onResourceReady(cached, DataSource.MEMORY_CACHE);return null;}//3、此处也可以理解为缓存逻辑EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);if (active != null) {cb.onResourceReady(active, DataSource.MEMORY_CACHE);return null;}//4、从map中获取一个EngineJob对象EngineJob<?> current = jobs.get(key);if (current != null) {current.addCallback(cb);return new LoadStatus(cb, current);}//5、创建一个EngineJob对象EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,useUnlimitedSourceExecutorPool);//6、创建一个DecodeJob对象    DecodeJob<R> decodeJob = decodeJobFactory.build(glideContext,model,//urlkey,//省略一大堆参数,transcodeClass,,engineJob);//将engineJob放入map缓存    jobs.put(key, engineJob);//添加一个callbackengineJob.addCallback(cb);//8、开始engineJobengineJob.start(decodeJob);//返回一个LoadStatus对象return new LoadStatus(cb, engineJob);}

load方法的代码很长,1至4是与缓存有关的逻辑,本篇暂时抛开不谈,在第五步的时候创建来一个EngineJob对象这是一个DecodeJob.Callback接口的实现类,后面会提到此处可以留意下,现在暂且可以理解为EngineJob是Engine这个引擎的最小工作单元。然后第六步又创建来一个DecodeJob对象(该对象是一个Runnable),第八步 engineJob.start(decodeJob);开始来工作,所以此处需要进入start方法探究一二:

 public void start(DecodeJob<R> decodeJob) {this.decodeJob = decodeJob;//获取一个GlideExecutor对象,为ThreadPoolExecutor的子类GlideExecutor executor = decodeJob.willDecodeFromCache()? diskCacheExecutor: getActiveSourceExecutor();//执行decodeJob这个ruanableexecutor.execute(decodeJob);}

EngineJob的start方法只是获取一个ThreadPoolExecutor对象执行DecodeJob这个Runnable而已(当然其内部还是比较复杂的),也就是说EngineJob只是一个壳子,其核心作用的还是DecodeJob这个对象。所以我们来看看DecodeJob这个Runalbe的run方法都做了些神马:

public void run() {DataFetcher<?> localFetcher = currentFetcher;try {if (isCancelled) {notifyFailed();return;}//runWrapped方法是重点runWrapped();} catch (RuntimeException e) {//省略部分代码  } finally {//省略部分代码}}

抛开run方法的枝枝蔓蔓,其内部调用来runWrapped方法,感觉距离真相越来越近了有木有,这是真的吗?:

  private void runWrapped() {switch (runReason) {//本篇分析这个分支case INITIALIZE:stage = getNextStage(Stage.INITIALIZE);currentGenerator = getNextGenerator();runGenerators();break;case SWITCH_TO_SOURCE_SERVICE:runGenerators();break;case DECODE_DATA:decodeFromRetrievedData();break;}}

上面有三个case分支,但是弱水三千只取一瓢,博主通过追踪内部逻辑准备只分析INITIALIZE分支,正如glide注释所说该分支INITIALIZE的意思是”The first time we’ve been submitted”,鉴于英语水平差,就不翻译了(事实上如果你打断点debug的话也会进入这个分支)。该case分支先调用了getNextGenerator方法,所以还是来看看该方法是干了啥牛逼的事儿,在分析runWrapped的时候有山重水复疑无路的感觉,现在感觉距离最终目的地尚有西天还有十万八千里呢!

 private DataFetcherGenerator getNextGenerator() {switch (stage) {case RESOURCE_CACHE://缓存相关return new ResourceCacheGenerator(decodeHelper, this);case DATA_CACHE://缓存相关return new DataCacheGenerator(decodeHelper, this);case SOURCE://分析该分支return new SourceGenerator(decodeHelper, this);case FINISHED:return null;}}

因为上文说过,本篇步分析与缓存有关的东西,只分析数据的最初来源(也就是服务器数据),所以本篇博文就分析SOURCE分支,该分支返回里一个SourceGenerator对象,该对象需要decodeHelper对象,decodeHelper对象是在初始化DecodeJob的时候对decodeHelper进行初始化的:

//初始化decodeHelperDecodeHelper<R> decodeHelper = new DecodeHelper<>();DecodeJob<R> init(GlideContext glideContext,Object model,//url//省略了一大堆参数) {//调用decodeHelper的init方法初始化相关参数decodeHelper.init(glideContext,model,//url//省略了一大堆参数);//省略了无关代码return this;}

调用完getNextGenerator方法之后currentGenerator引用指向的对象就是SourceGenerator对象了,紧接着INITIALIZE的case分支又调用了runGenerators() 方法:

private void runGenerators() {boolean isStarted = false;while (!isCancelled && currentGenerator != null&& !(isStarted = currentGenerator.startNext())) {stage = getNextStage(stage);//链式调用currentGenerator = getNextGenerator();if (stage == Stage.SOURCE) {reschedule();return;}}//省略了部分代码}

上面的方法中有一个while循环,while循环的条件题里面有一句:

//currentGenerator为SourceGenerator对象
isStarted = currentGenerator.startNext()

如果该方法执行返回了false,则满足进入循环的条件之一,所以我们看看SourceGenerator的startNext都多了些什么:

 public boolean startNext() {//省略了缓存逻辑loadData = null;boolean started = false;while (!started && hasNextModelLoader()) {//1 、获取一个loadData对像获取LoadData对象loadData = helper.getLoadData().get(loadDataListIndex++);//省略部分代码//调用loadData的fetcher对象获取数据loadData.fetcher.loadData(helper.getPriority(), this);}return started;}

上面的代码主要做了两个逻辑:
1、先通过getLoadData获取ModelLoder对象的集合,然后获取一个loadData对象
2、通过loadData对象的fetcher对象的loadData方法来获取图片数据。
其实这里简单打个断点,然后debug一下就知道LoadData对象和fetcher都是什么:

上面说的在这里先埋疑问:
ModeLoder 是什么?什么时候初始化的?
LoadData 有是什么时候初始化的?
Fetcher有是干什么,有是怎么初始化的?
以上三个问题将留在下篇博客说明,本篇为了保持主题不偏方向,先不谈。

这个LoadData是ModelLoader接口的一个内部类:

 class LoadData<Data> {public final Key sourceKey;public final List<Key> alternateKeys;//真正获取数据的类public final DataFetcher<Data> fetcher;}

loaddata 对象内部有一个DataFetcher,该类就是Glide实际获取数据的类,如上图我们得到的是一个HttpUrlFetcher 对象,所以我们直接进入HttpUrlFetcher对象的loadData方法看看:

public void loadData(Priority priority, DataCallback<? super InputStream> callback) {final InputStream result;//发起网络请求,讲URL对应的数据转换成InputStreamresult = loadDataWithRedirects(glideUrl.toURL(), 0     , null ,glideUrl.getHeaders());//将图片数据输入流交给callback处理callback.onDataReady(result);//第一个callback}

loaddata方法是真正获取图片数据的地方:
1、调用loadDataWithRedirects方法,发起网络请求,讲URL指定的图片数据转换成InputStream输入流,该方法内部就是通过URLConnection对象来完成的(详细的读者可以自行到HttpUrlFetcher类的loadDataWithRedirects查看)。
2、将图片数据InputStream通过callback的onDataReady来传递,此处是我们遇到的第一个callback

也就是说通过HttpUrlFecther处理之后,我们的图片数据源转换成来InputStream对象了。
那么上文中的callback 是什么呢?,该callback是一个DataCallback类型的接口(这是我们遇到的第一个callback,后面还有好多,希望读者不要绕晕了)。HttpUrlFecther的loadData方法的第二个参数就是一个DataCallback类型的参数,在SourceGenerator种我们传的是this,也就是说我们这个callback就是SourceGenerator对象,所以看看SourceGenerator对象的onDataReady方法都做了什么:

   //第二个callbackprivate final FetcherReadyCallback cb;//此处data就是InputStreampublic void onDataReady(Object data) {//省略缓存逻辑//第二个callbackcb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,loadData.fetcher.getDataSource(), originalKey);}}

剔除掉缓存的相关逻辑后,我们迎来了第二个callback,调用了该回调的onDataFetcherReady方法后,我们又将数据传给了这个callback。那么这个callback有是什么呢?该callback是FetcherReadyCallback的一个接口实现。那么这个callback 又是什么时候初始化的呢?当然是初始化SourceGenerator对象的时候,见上文我们是在DecodeJob这个类种初始化SourceGenerator的,DecodeJob就是这个callback的实现类,所以我们进入其onDataFetcherReady方法看看:

  @Overridepublic void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,DataSource dataSource, Key attemptedKey) {//将数据交给DecodeJob的currentData持有this.currentData = data;//InputStream//省略部分代码decodeFromRetrievedData();}}

然后我们看看decodeFromRetrievedData方法:

 private void decodeFromRetrievedData() {//1、将Inpustream转换成Resource对象Resource<R> resource = resource = decodeFromData(currentFetcher, currentData, currentDataSource);//2、将数据继续传递if (resource != null) {notifyEncodeAndRelease(resource, currentDataSource);} else {runGenerators();}}

上面的方法做了两件事:
1、将图片数据InputStream流转换成Resource对象
2、调用notifyEncodeAndRelease将数据继续传递。
此时我们的图片数据由InputStream转换成了Resource对象。看看notifyEncodeAndRelease都做了写什么:

  private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {Resource<R> result = resource;//省略部分代码notifyComplete(result, dataSource);//省略部分代码}}

省略了与本文无关的代码之后,如上面所示我们来到了DecodeJob类的notifyComplete方法:

 private Callback<R> callback;
private void notifyComplete(Resource<R> resource, DataSource dataSource) {//省略一行代码//第三个callbackcallback.onResourceReady(resource, dataSource);}

notifyComplete方法中我们见到了第三callback,并且调用其onResourceReady方法,那么这个 callback的具体实现类是什么呢?该对象通过观察源码发现是在初始化DecodeJob初始化的,而根据上文DecodeJob的初始化是在Engine 的load方法里面完成的,在该类初始化的时候我们还初始化了EngineJob这个类,这个类就是这个第三个callback的具体实现(见上文engine.load 的讲解),所以看看EngineJob的onResourceReady方法:

  public void onResourceReady(Resource<R> resource, DataSource dataSource) {//原始数据this.resource = resource;this.dataSource = dataSource;MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();}

finally,见到了Handler的身影,说明我们距离终点不远了,上文的handler发送了MSG_COMPLETE消息该handler来处理,handler的初始化如下:

private static final Handler MAIN_THREAD_HANDLER =new Handler(Looper.getMainLooper(), new MainThreadCallback());

所以我们进入MainThreadcallback来看看MSG_COMPLETE消息的处理逻辑是什么,关于handler的具体工作流程参考博主《android消息处理机制详解》:

 //MainThreadcallbackpublic boolean handleMessage(Message message) {EngineJob<?> job = (EngineJob<?>) message.obj;switch (message.what) {case MSG_COMPLETE:job.handleResultOnMainThread();break;}return true;}

仅仅是调用了EngineJob的handleResultOnMainThread方法,此时我们已经将图片数据切换到了UI线程:

  void handleResultOnMainThread() {//省略部分代码//将数据转换成engineResourceengineResource = engineResourceFactory.build(resource, isCacheable);hasResource = true;//省略部分代码for (ResourceCallback cb : cbs) {if (!isInIgnoredCallbacks(cb)) {//特么的第四个callbackcb.onResourceReady(engineResource, dataSource);}}}

该方法做了两件事:
1、将resource交给engineResource来持有
2、将engineResource交给本文的**第四个callback**

那么这第四个callback又是什么?是ResourceCallback接口,该接口是通过初始化EngineJob的时候调用EngineJob的 addCallback(ResourceCallback cb)方法传进来的。且EngineJob 的初始化是在Engine的load 方法中,而load 的方法的一个参数就是又这个callback, 且load的调用又是在文章开头的SingleReqeust方法,顺藤摸瓜发现第四个callback就是SingleReqeust,所以我们进入该SingleReqeust的onResourceReady方法:

 public void onResourceReady(Resource<?> resource, DataSource dataSource) {//调用重载方法:onResourceReady((Resource<R>) resource, (R) received, dataSource);}private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {this.resource = resource;//最终调用target的onResourceReadytarget.onResourceReady(result, animation);}

如果你读过博主的《Glide工作总体执行流程概述》这篇文章的话,就可以知道此处的target就是DrawableImageViewTarget,该target的onResourceReady方法的最总逻辑就是调用:

imageView.setImageDrawable(resource);

来完成图片的展示。

到此为止,本篇分析完毕,分析源码的过程中四个callback来回调度可把我绕坏了,为了本篇博文的主题连续性,基本上省略了好多东西没讲,什么ModelLoader啦,LoadData啦,Fetcher啦,将在后续博文中讲解,敬请期待,如有不当之处,欢迎批评指正

Glide 4.x之请求网络图片数据流程解析相关推荐

  1. JS中的Ajax发送请求获取数据流程

    前言: JS两个常用的请求方法 [XMLHttpRequest() .fetch()] XMLHttpRequest() 的使用方法大致可以分为四步: 1.创建XMLHttpRequest的对象成员 ...

  2. unity请求json数据并解析

    unity3d在跟.net进行http通信的时候,最常见的就是表单数据的提交请求了,但服务器端会返回一坨json数据,这就要求我们在unity中进行json数据的处理了,一般unity中处理json个 ...

  3. ajax请求json和xml数据及对json和xml格式数据的解析

    ajax请求json和xml数据及对json和xml格式数据的解析 一.ajax请求json数据并解析 ajax的写法: json数据解析: 请求json经常出现的跨域报错: 二.ajax请求xml数 ...

  4. SpringBoot+MyBatisPlus实现前端传递时间查询条件ajax请求后台并回显数据流程整理

    场景 前端时间选择控件,选择时间后点击搜索,请求后台数据,后台根据时间查询数据库中 一天的记录数并回显给前端,前端进行显示. 实现 前端页面代码(部分) <div class="ibo ...

  5. etcd 笔记(05)— etcd 代码结构、各模块功能、整体架构、各模块之间的交互、请求和应答流程

    1. etcd 项目结构和功能 etcd 项目代码的目录结构如下: $ tree ├── auth ├── build ├── client ├── clientv3 ├── contrib ├── ...

  6. 面试官:说说Kafka处理请求的全流程

    今天来讲讲 Kafka Broker端处理请求的全流程,剖析下底层的网络通信是如何实现的.Reactor在kafka上的应用. 再说说社区为何在2.3版本将请求类型划分成两大类,又是如何实现两类请求处 ...

  7. HBase - 数据写入流程解析

    本文由  网易云 发布. 作者:范欣欣 本篇文章仅限内部分享,如需转载,请联系网易获取授权. 众所周知,HBase默认适用于写多读少的应用,正是依赖于它相当出色的写入性能:一个100台RS的集群可以轻 ...

  8. Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据流程分析【转】

    Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据&流程分析 QQ空间说说抓取难度比较大,花了一个星期才研究清楚! 代码请移步到GitHub GitHub地址:http ...

  9. HDFS的工作机制,HDFS写数据流程,HDFS读数据流程(来自学习资料)

    4.hdfs的工作机制 (工作机制的学习主要是为加深对分布式系统的理解,以及增强遇到各种问题时的分析解决能力,形成一定的集群运维能力)   注:很多不是真正理解hadoop技术体系的人会常常觉得HDF ...

  10. Hadoop之HDFS读写数据流程

    Hadoop之HDFS读写数据流程 目录 HDFS写数据流程 HDFS读数据流程 网络拓扑概念 机架感知 1. HDFS写数据流程 HDFS写数据流程,如下图 客户端通过Distributed Fil ...

最新文章

  1. git-【五】远程仓库
  2. 独立软件开发商进军SaaS注意八个问题,互联网营销
  3. JavaScript面向对象编程——Array类型
  4. android微信支付的实现
  5. php7.0 java 性能,php7代码性能常见优化技巧
  6. [蓝桥杯2018初赛]第几个幸运数-数论+枚举
  7. 6-2 删除单链表偶数节点 (10 分)
  8. 数据不平衡问题及解决方案
  9. 原生js发送ajax请求
  10. Python项目:Django员工管理系统
  11. Windows 技术篇-修改电脑时间格式显示为12小时、24小时时间制式方法
  12. 【ELK解决方案】ELK集群+RabbitMQ部署方案以及快速开发RabbitMQ生产者与消费者基础服务...
  13. [NLP]OpenNLP文档分类器的使用
  14. 【广告算法工程师入门 26】机制设计-考虑客户ROI的机制设计方法与实践
  15. 小县城开什么店比较挣钱?
  16. 元宇宙正在模糊 “虚拟” 和 “现实” 之间的界限
  17. C/C++编程学习 - 第19周 ⑩ 派
  18. selenium爬取评论
  19. 联想台式计算机内存哪个好,联想台式机哪款好 联想台式机家悦3000评测【详解】...
  20. 【OWA】03安装部署:OWA(Office Web Apps)安装和部署

热门文章

  1. kx3552驱动最佳连线图_意甲新赛季5大看点:C罗连线苏牙,皮尔洛执教初体验
  2. 08. Django基础:模板层
  3. 正则表达式:JS在一段HTML文字中找出所有img标签的src属性
  4. C#:获得本机IP地址
  5. linux系统下,traceroute路由跟踪指令详解
  6. 低照度图像修复方法总结
  7. 基于随机森林的姿态识别算法
  8. Java虚拟机知识点【栈帧】
  9. bzoj千题计划175:bzoj1303: [CQOI2009]中位数图
  10. Scala Singleton对象