Glide 系列(三) Glide源码整体流程梳理

目录.jpg

闲聊

回顾了一下历史文章,不知不觉,我们安卓兽已经坚持整整一年时间来维护我们的公众号了,陆陆续续一共发布了7个系列的文章。总觉得不管是对于团队,还是个人,这都是一件非常有意义的事儿。无所谓关注度,无所谓点赞数,无所谓打赏数,只要安卓兽的成员们能够持续成长,记录下自己的成长历程,就好了。
在新的一年,安卓兽们,继续加油~~~

前言

在前面几个不同系列的源码分析之后,今天我们终于来到图片加载框架的源代码分析。先说说为什么要选择Glide,而不选择其他,其实理由说起来很简单也很粗暴:Google推荐,没毛病!
相比较前面几个系列的源代码,Glide的源代码读起来确实还是有些麻烦的,里面涉及的类众多而繁杂,调用链条错综而冗长。如果真的想把每一行代码搞清楚,那势必会花费超长的时间。因此对于Glide的源码分享,我们的整体计划是:本篇先做一个整体的流程梳理,让大家知道Glide在图片加载过程中,到底做了哪几件大事,围绕每件大事又做了哪几件小事;然后接下来的几篇文章,会针对各个特性,进行针对性介绍。
那么现在就开始我们的介绍吧,本篇文章主要基于Glide 3.7.0版本。

主流程梳理

虽然Glide代码量比较大,但经过我们的抽丝剥茧,Glide在图片加载过程中,其实主要只做了几件大事。把这几件大事先梳理清楚,非常有助于我们对于源代码的阅读。具体可参考下图:


主流程图.jpg

三件大事:

  • “准备数据”。Glide中存在着大量的类和对象,而Glide的做法是在最开始的时候,都尽量把各种将来要用的对象就构造出来,并按需封装起来,然后层层向后面的流程进行传递。漫长的调用链条中,时不时就要用到最开始就创建好的对象,并且可能经过层层的解封装才能拿到真正想要用到的对象。也正是因此,使得读者往往迷失其中,造成了Glide源代码阅读起来较为困难。
    但仔细阅读代码,还是可以看出规律,“准备数据”其实可以划分两件小事:第一阶段,构建出GenericRequest对象,该对象封装了大量对象于一身,并且这些对象受用户调用API或者修改配置所影响;第二阶段,从GenericRequest对象中,解封装拿到需要的对象,构建出decodeJob对象,该对象是“异步处理”中的核心对象。可以这样理解:GenericRequest对象面向用户而构建,decodeJob对象面向Glide而构建。
  • “异步处理”。经过前面的大量准备工作,这一步,在工作线程中,Glide就要开始大干拳脚了。可以划分为三件小事:发起网络请求,拿到数据流;将数据流解码成bitmap对象;将bitmap对象转码成Drawable对象。
  • “切换到主线程”:满足要求的Drawable对象已经拿到,那么我们就要切换回主线程,目的是将其显示出来。这一步主要就是一件小事:显示Drawable。

经过这一番图文讲解,Glide的主流程是不是更清晰了些?对的,万变不离其宗,Glide也一样的,显示图片不过就是经过这么几步而已。

主流程细化

接下来,我们对于主流程进行进一步细化。
对于用户来说,想要显示一张图,只需要简单的调用下面的代码就可以了。

Glide.with(this).load(url).into(imageView);

那么我就从这三个函数入手,看看Glide是如何通过这三个函数,最终跑通以上主流程的。
注意,以下依然通过图文的方式进行介绍,以方便读者理解。另外,以下时序图非常详细,几乎包含了源代码调用的每一处细节,比较难理解的对象,我也基本都写了简单的注释,方便读者追溯其由来,从而疏通整个调用流程。所以我就不贴太多源代码了,读者如果有不清楚的地方,对照一下源代码,应该就可以理解了。

with

with属于“准备数据”之“第一阶段”,我们先展示其时序图:


时序图--with().jpg

从上图我们可以看出,with的代码流程,还是比较简单,最终会返回一个RequestManager类型的对象,以供下一步调用。
另外需要说明的是:

  • with中传入的参数,决定了图片加载的生命周期。
  • 如果在工作线程中使用Glide,不管with中传入参数是什么,Glide生命周期都与Application对象的生命周期一致。

想特别了解的同学可以看一下源码。

load

load属于“准备数据”之“第一阶段”,我们先展示其时序图:
时序图--load().jpg

从上图我们可以看出,load的代码流程,稍微复杂了一些,但是实质上还是做前期的数据准备,主要就是构造对象,封装对象。
另外需要说明的是:

  • load中的参数可以多种多样,这里我们以传入String对象为例,进行讲解。
  • 图中的streamModelLoader实际上是StreamStringLoader,没有注释由来。简单说一下:主要是因为在Glide对象的构造过程中就注册了对应关系,这里只是通过这层对应关系,找到了StreamStringLoader对象而已。在后面的“替换组件”章节,还会提到这一段代码的。
register(String.class, InputStream.class, new StreamStringLoader.Factory());

into

into比较复杂,其涉及了“准备数据”,“异步处理”,“切换到主线程”这三大步的内容。因为其内容较多,一张时序图实在展示不下,我们会分解成5张时序图,来顺序进行说明。

into-1

into-1涉及了“准备数据”的“第一阶段”和“第二阶段”,我们先展示其时序图:


时序图--into()-1.jpg

从上图我们可以看出,into-1的代码流程,实质上还是做数据准备,主要就是构造对象,封装对象。需要关注的是其构造出了GenericRequest对象。
另外需要说明的是:

  • 最后调用的into函数接收Target类型的对象,也就是说我们在外部使用Glide时候,可以一步到位,不必传ImageView类型的对象,而直接传递Target类型的对象。而Target实际上是一个接口,可以让我知道图片加载过程中的各种状态,如开始,结束,失败等等。
  • buildRequest函数,最终将所有相关的对象,都封装到一个单一的对象中,即GenericRequest类型的对象。至此“准备数据”的“第一阶段”完成。从begin函数起,进入“准备数据”的“第二阶段”,此阶段会以GenericRequest对象为单位进行后续流程。
  • onLoadStarted和onLoadFailed函数可以显示用户设置的占位符。
  • 如果使用了override() API为图片指定了一个固定的宽高,那么直接调用onSizeReady函数;如果没有指定,那么调用getSize函数,由Glide帮我们计算出宽高,但最终还是会调用会onSizeReady函数。所以这张图的出口就是onSizeReady函数。

into-2

into-2属于“准备数据”之“第二阶段”,并最终开启了“异步调用”的大门,先展示其时序图:


时序图--into()-2.jpg

从上图我们可以看出,into-2的代码流程,实质上还是做数据准备,主要就是构造对象,封装对象。需要注意的是,这里开始使用“第一阶段”生成的GenericRequest对象,从GenericRequest对象取出各种需要的对象,传递给Engine的load函数,最终构造出了decodeJob对象。
另外需要说明的是:

  • decodeJob对象是下一步“异步调用”的核心对象,“发起网络请求,拿到数据流”、“将数据流解码成bitmap对象”和“将bitmap对象转码成Drawable对象”,都是从decodeJob对象中发起的。在接下来的into-3和into-4中,读者就可以看到。
  • 图中的StreamStringLoader的getResourceFetcher函数返回的是HttpUrlFetcher对象,没有注释由来。简单说一下:主要还是因为在Glide对象的构造过程中就注册了多个对应关系,经过三个对应关系的链式传递,最终返回了HttpUrlFetcher对象。这里贴一下简单的代码,有兴趣的同学,可以根据此线索追踪一下:
register(String.class, InputStream.class, new StreamStringLoader.Factory());
register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
public static class Factory implements ModelLoaderFactory<String, InputStream> {
    @Override
    public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) {
        return new StreamStringLoader(factories.buildModelLoader(Uri.class, InputStream.class));
    }
}
public static class Factory implements ModelLoaderFactory<Uri, InputStream> {
    @Override
    public ModelLoader<Uri, InputStream> build(Context context, GenericLoaderFactory factories) {
        return new StreamUriLoader(context, factories.buildModelLoader(GlideUrl.class, InputStream.class));
    }
}
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
    @Override
    public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
        return new HttpUrlGlideUrlLoader(modelCache);
    }
}
public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        return new HttpUrlFetcher(url);
    }
}

into-3

into-3和into-4可以连在一起看。into-3属于“异步处理”之“发起网络请求,拿到数据流”和“将数据流解码成bitmap对象”,先展示其时序图:


时序图--into()-3.jpg

从上图我们可以看出,into-3的代码流程,主要完成了“发起网络请求,拿到数据流”和“将数据流解码成bitmap对象”两件小事。
另外需要说明的是:

  • 该时序图涉及的流程中,有些对象比较难追溯,我都写了相关注释,方便读者阅读源代码。
  • 拿到bitmap对象之后,并没有直接返回bitmap对象,而是进行了封装,其目的就是为了封装之后,Bitmap图片和Gif图片可以统一处理。

into-4

into-4和into-3可以连在一起看。into-4属于“异步处理”之“将bitmap对象转码成Drawable对象”,先展示其时序图:


时序图--into()-4.jpg

从上图我们可以看出,into-4的代码流程,主要完成了“异步处理”中的第三件小事:“将bitmap对象转码成Drawable对象”
另外需要说明的是:

  • 该时序图涉及的流程中,有些对象比较难追溯,我都写了相关注释,方便读者阅读源代码。
  • 转码的原因:只有Bitmap或者Drawable才能显示到ImageView上,而Bitmap转换成Drawable是为了保证静图和动图的类型一致性(动图的类型是Drawable),逻辑上好处理。具体可以参考如下代码:
public Resource<GlideDrawable> transcode(Resource<GifBitmapWrapper> toTranscode) {
    GifBitmapWrapper gifBitmap = toTranscode.get();
    Resource<Bitmap> bitmapResource = gifBitmap.getBitmapResource();

    final Resource<? extends GlideDrawable> result;
    if (bitmapResource != null) {
        result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);
    } else {
        result = gifBitmap.getGifResource();
    }
    // This is unchecked but always safe, anything that extends a Drawable can be safely cast to a Drawable.
    return (Resource<GlideDrawable>) result;
}

public Resource<Bitmap> getBitmapResource() {
    return bitmapResource;
}

public Resource<GifDrawable> getGifResource() {
    return gifResource;
}

into-5

into-5属于“切换到主线程”之“显示Drawable对象”,先展示其时序图:


时序图--into()-5.jpg

从上图我们可以看出,into-5的代码流程,主要完成了在主线程中显示Drawable对象
另外需要说明的是:

  • 该时序图涉及的流程中,有些对象比较难追溯,我都写了相关注释,方便读者阅读源代码。
  • 通过Handler机制,Glide从工作线程切换到主线程,并最终将Drawable对象显示到ImageView上。

总结

经过一番图文讲解,读者是不是对于Glide源码整体流程有了大致的了解。如果想进一步了解的话,建议读者跟着时序图,走一遍源代码,应该会收获更多。
对于阅读复杂的源码,分享给大家一点经验:先从框架或者主流程入手,然后再逐个细化各个模块,最后再想想作者为什么要这么设计,我们能学习或者借鉴到什么。
好了,这一篇就到这里吧,接下来的几篇文章,会针对各个特性,进行针对性介绍,敬请期待。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容