写在之前

上一篇博文写的是Picasso基本使用和源码完全解析,Picasso的源码阅读起来还是很顺畅的,然后就想到Glide框架,网上大家也都推荐使用这个框架用来加载图片,正好我目前的写作目标也是分析当前一些流行的框架源码,那就也来解析下Glide的源码吧,而且有了Picasso源码的分析相信很快就搞定Glide的,结果也就悲剧了,深陷其中无法自拔了,Glide的源码远非Picasso能比,阅读起来也是相当的困难的,而且我使用的是最新的Glide4.0,与之前版本有较大的差异,网上也没可以参考的资料,这就悲剧了,苦头专研呗。直到今天才从深沟中冒出头了,差点憋死,哈哈。

正文

Glide的使用和Picasso基本一样,这里就不再多说,主要是因为源码分析会写的很长很细,再加上基本使用的话,就更加长了,而且上一篇已写过Picasso的基本使用,这两者在使用方面相差的微乎及微,所以我们这篇文章直接进入源码分析。

Glide 源码分析

首先在build.gradle里添加如下引用:

compile 'com.github.bumptech.glide:glide:4.0.0-RC0'

这里我用的是Glide4.0也是最新的版本,和3.X在源码上还是有很大的差别的。看过3.X源码的相信对比下这篇文章就会知道。

ok,和Picasso的分析模式一样,我们也是从下面最简单的代码进行一步一步的深入分析:

Glide.with(MainActivity.this).load(url).into(headerImage);

with()

首先我们来看看当我们调用Glide的with方法那做了哪些工作:

with方法中可以接受Context,Activity,FragmentActivity,Fragment甚至是View不同的类型,返回的是RequestManager对象,这个对象是需要在RequestManagerRetriever中获取的,那我们在来看看RequestManagerRetriever是怎么获取到的?请看getRetriever方法的源码:

getRetriever方法中也没有真正的创建RequestManagerRetriever对象,而是从Glide的getRequestManagerRetriever方法中获取,那么很明显的可以看出所做的工作都是在Glide的get方法中完成的,在来看下get方法源码:

来到这一步,我们看到了非常熟悉的代码设计原理,那就是双重加锁的单例模式保证Glide对象的唯一性,那么initGlide就是创建Glide对象的方法了,请看:

这是initGlide方法中最重要的代码,主要是创建了一个GlideBuilder对象,然后调用build方法来完成Glide对象的创建,相信不用看bulid方法,大家也会猜到接下来将要发生什么样的事情,没错,那就是使用建造者设计模式来完美的构建出Glide对象:

public Glide build(Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
} if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
} if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
} if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
} if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
} if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
} if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
} if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor());
} RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(
requestManagerFactory); return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock());
}

build方法中主要是构建线程池(包括sourceExecutor ,diskCacheExecutor ),缓存大小和缓存器,默认的连接监听工厂(connectivityMonitorFactory ),Engine对象和RequestManagerRetriever 对象等等。

有几个重要的对象创建,我们这里先看下它的构建内容:

1 Engine对象

创建Engine对象在构造方法中传递了几个重要的参数,分别是线程池,内存缓存和硬盘缓存对象,那我们来看看在构造方法中它是怎么构建Engine对象的:

Engine(MemoryCache cache,
DiskCache.Factory diskCacheFactory,
GlideExecutor diskCacheExecutor,
GlideExecutor sourceExecutor,
GlideExecutor sourceUnlimitedExecutor,
Map<Key, EngineJob<?>> jobs,
EngineKeyFactory keyFactory,
Map<Key, WeakReference<EngineResource<?>>> activeResources,
EngineJobFactory engineJobFactory,
DecodeJobFactory decodeJobFactory,
ResourceRecycler resourceRecycler) {
this.cache = cache;
this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory); if (activeResources == null) {
activeResources = new HashMap<>();
}
this.activeResources = activeResources; if (keyFactory == null) {
keyFactory = new EngineKeyFactory();
}
this.keyFactory = keyFactory; if (jobs == null) {
jobs = new HashMap<>();
}
this.jobs = jobs; if (engineJobFactory == null) {
engineJobFactory = new EngineJobFactory(diskCacheExecutor, sourceExecutor,
sourceUnlimitedExecutor, this);
}
this.engineJobFactory = engineJobFactory; if (decodeJobFactory == null) {
decodeJobFactory = new DecodeJobFactory(diskCacheProvider);
}
this.decodeJobFactory = decodeJobFactory; if (resourceRecycler == null) {
resourceRecycler = new ResourceRecycler();
}
this.resourceRecycler = resourceRecycler; cache.setResourceRemovedListener(this);
}

创建了几个工厂对象方法,比如EngineKeyFactory,EngineJobFactory和DecodeJobFactory,几个HashMap类型的对象集合,如:jobs ,activeResources 等等,然后就分别把这些对象赋值给Engine的成员变量,那么来看下创建Engine对象时到底初始化了那些成员变量:

ok,Engine是一个非常重要的对象,后面扮演着重要的角色,为了方面理解它所拥有那些可使用的对象,这里我做了个类图显示的标了出来。

2 RequestManagerRetriever 对象

再来看看RequestManagerRetriever 对象的创建,这个相对的简单很多,我们来看下它的构造方法:

由于我们传递过来的requestManagerFactory为空,所以factory将会使用默认的DEFAULT_FACTORY工厂,DEFAULT_FACTORY是真正创建RequestManager对象的地方,稍后介绍。

这里只是让大家知道这里的factory就是DEFAULT_FACTORY。

来看看它拥有哪些成员:

3 Glide对象

在build方法中 return new Gilde(),创建一个Glide对象并返回,那在Gilde构造方法中做了哪些初始化工作呢?

Glide(
Context context,
Engine engine,
MemoryCache memoryCache,
BitmapPool bitmapPool,
ArrayPool arrayPool,
RequestManagerRetriever requestManagerRetriever,
ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
RequestOptions defaultRequestOptions) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory; DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat); final Resources resources = context.getResources(); registry = new Registry();
registry.register(new DefaultImageHeaderParser()); registry.register()...append()... ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext = new GlideContext(context, registry, imageViewTargetFactory,
defaultRequestOptions, engine, this, logLevel);
}

这里首先是把传递进来的参数赋值给成员变量,然后创建了几个重要的对象:

①:Registry对象

Registry主要是添加很多的注册或解析方式等,这在后面用来解析是从内存,文件或是网络获取资源有着重要的作用,而且它每一类解析方式都会提供多个方法,一种方式获取不到将会使用另外一种,知道获取到资源为止,来看下它的register和append方法:

主要是存放到不同的对象中的集合变量中。

②:GlideContext对象

GlideContext对象在后面也扮演中重要的角色,创建这个对象到目前为止只是为了初始化和赋值:

总结下Glide,Registry和GlideContext对象所初始化的参数:

这是到目前对象所拥有的成员方法和成员变量。

ok,再回到上面build方法,在返回Glide对象后,调用getRequestManagerRetriever()从而获取到RequestManagerRetriever对象,从上面Glide类图我们也可以看出,Glide对象已包含RequestManagerRetriever对象。

再往上返回一步,在getRetriever(activity)方法中获取到RequestManagerRetriever对象后,调用get(activity)来获取RequestManager对象,那么我们来看看它是怎么获取到的?

首先判断是否在子线程执行,否则就调用supportFragmentGet方法来获取RequestManager对象,那么来看下它的源码:

还记得我们的RequestManagerRetriever拥有哪些成员吗,不记得就去看看上面它的类图吧,由它的源码我们可以看到它将会使用factory并调用它的build方法,还记得factory是什么吗?上面已分析factory就是DEFAULT_FACTORY,那来看看它的源码实现:

在build中创建一个RequestManager对象并返回,来看下RequestManager的构造方法中做了哪些操作:

 RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker; final Context context = glide.getGlideContext().getBaseContext(); connectivityMonitor =
factory.build(context, new RequestManagerConnectivityListener(requestTracker)); if (Util.isOnBackgroundThread()) {
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor); setRequestOptions(glide.getGlideContext().getDefaultRequestOptions()); glide.registerRequestManager(this);
}

主要是赋值,添加生命周期监听器,设置请求属性,以及注册请求管理器,代码还是很简单的,都能看的明白。

来看下它的类图:

ok,到此,我们的with方法中获取RequestManager对象就已完成。

来看下with方法执行的顺序图:

注:流程图看不清楚,可以选择在“新标签中打开图片”查看。

load()

在调用with方法获取到RequestManager对象的前提下,调用load方法,并传递我们的url参数,来看下它的源码:

这里并没有直接的去加载我们的url获取资源,而是首先调用asDrawable方法来配置图片的信息,其实就是说加载获取的图片资源是转换为drawale或是bitmap或是gif进行图片显示,默认的是使用drawale,你也可以使用asGif()/asBitmap()来设置它是已什么形式来展示。这里我们按默认的方式来分析。

load方法目的是为了获取RequestBuilder对象,下面来一步步分解它源码:

首先来看下asDrawable()的源码:

asDrawable()中首先调用as方法,并传进Drawable.class作为参数,来看下as方法的源码:

由as方法,我们可看到它直接的创建一个RequestBuilder对象,并传递了相关的参数进去,这里要注意下resourceClass就是Drawable.class这里在后面有个选择分支时使用到。

来看下RequestBuilder的构造方法中做了哪些初始化布局。

很简单的赋值,这里也需要注意的是transcodeClass就是Drawable.class类。

ok,获取到RequestBuilder对象后,它又做了进一步的赋值操作,就是在transition方法中,

把创建的DrawableTransitionOptions对象赋值给transitionOptions变量。

ok,再往上来看,完成asDrawable方法对RequestBuilder的创建后才调用load方法来传递我们的url地址,其实在load也没有做什么事,就是一个中转站,转给了loadGeneric方法,来看:

在loadGeneric方法中也没做其他太多的操作,也是保存了我们的url并且isModelSet设置为true,意思就是说Model已有设置了。来看下它的类图:

它到目前为止所包含的成员变量和方法都在此。

ok,到此我们的load方法也分析完毕,来看下它的流程图:

注:流程图看不清楚,可以选择在“新标签中打开图片”查看。

由于Glide源码很是复杂,写的很长,所以只能分两篇来发布,第一篇分析了Glide的with和load方法源码,第二篇将会分析into方法,说实在的into方法复杂程度远超过with和load方法总和,但是没关系,还是保持一贯的风格,一步步的分析其执行流程,相信大家学完肯定能完全的掌握它的源码结构。

ok,今天就先发布这一篇吧。

各位如果还有哪里不明白的,或是我这里讲的还不够透彻,亦或是讲错了的地方请留言指正,让我们共同进步,谢谢

同时,请大家扫一扫关注我的微信公众号,虽然写的不是很勤,但是每一篇都有质量保证,让您学习到真正的知识。

Android 图片加载框架Glide4.0源码完全解析(一)的更多相关文章

  1. Android 图片加载框架Glide4.0源码完全解析(二)

    写在之前 上一篇博文写的是Android 图片加载框架Glide4.0源码完全解析(一),主要分析了Glide4.0源码中的with方法和load方法,原本打算是一起发布的,但是由于into方法复杂性 ...

  2. Android 图片加载框架 Glide4.x

    概述 Glide是一个图片加载框架,使得我们可以轻松的加载和展示图片 Glide4.x新增apply()来进行设置,apply可以调用多次,但是如果两次apply存在冲突的设置,会以最后一次为准 新增 ...

  3. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API.还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),G ...

  4. Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

    本篇将是我们这个Glide系列的最后一篇文章. 其实在写这个系列第一篇文章的时候,Glide就推出4.0.0的RC版了.那个时候因为我一直研究的都是Glide 3.7.0版本,再加上RC版本还不太稳定 ...

  5. Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能

    我们的Glide系列文章终于要进入收尾篇了.从我开始写这个系列的第一篇文章时,我就知道这会是一个很长的系列,只是没有想到竟然会写这么久. 在前面的六篇文章中,我们对Glide的方方面面都进行了学习,包 ...

  6. Android图片加载框架最全解析(六),探究Glide的自定义模块功能

    不知不觉中,我们的Glide系列教程已经到了第六篇了,距离第一篇Glide的基本用法发布已经过去了半年的时间.在这半年中,我们通过用法讲解和源码分析配合学习的方式,将Glide的方方面面都研究了个遍, ...

  7. Android图片加载框架最全解析(五),Glide强大的图片变换功能

    大家好,又到了学习Glide的时间了.前段时间由于项目开发紧张,再加上后来又生病了,所以停更了一个月,不过现在终于又可以恢复正常更新了.今天是这个系列的第五篇文章,在前面四篇文章的当中,我们已经学习了 ...

  8. Android图片加载框架最全解析(四),玩转Glide的回调与监听

    大家好,今天我们继续学习Glide. 在上一篇文章当中,我带着大家一起深入探究了Glide的缓存机制,我们不光掌握了Glide缓存的使用方法,还通过源码分析对缓存的工作原理进行了了解.虽说上篇文章和本 ...

  9. Android图片加载框架最全解析(三),深入探究Glide的缓存机制

    在本系列的上一篇文章中,我带着大家一起阅读了一遍Glide的源码,初步了解了这个强大的图片加载框架的基本执行流程. 不过,上一篇文章只能说是比较粗略地阅读了Glide整个执行流程方面的源码,搞明白了G ...

随机推荐

  1. 在同一个系统上装两个不同版本的jdk,配置环境变量不起作用,jdk版本的切换问题

    本人这台笔记本前面装了jdk8,现在准备用jdk7,我安装好了jdk7:把系统变量中的JAVA_HOME 改为 D:\java\jdk\jdk7\jdk1.7.0_67,Path 下添加如下变量,记得 ...

  2. Linux IO barrier

    I/O顺序问题是一个比较综合的问题,它涉及的层次比较多,从VFS page cache到I/O调度算法,从IO子系统到存储外设.而Linux I/O barrier就是其中重要的一部分. 可能很多人认 ...

  3. 高性能日志类KLog(已开源代码)

    项目开源地址:https://github.com/ihambert/KLog  上回介绍了超简易日志类,但他有诸多的局限性,注定了不能作为一个网站的日志类. 那什么样的日志类才能用于网站呢.首先来假 ...

  4. [转]ObjectARX二次开发vs编译器版本ARX版本对应说明

  5. 1004 Let the Balloon Rise

    Contest time again! How excited it is to see balloons floating around. But to tell you a secret, the ...

  6. 如何使用angular-ui的弹出框

    在开发项目时,我们经常性的会遇到弹出框的需求,例如登陆,注册,添加信息等等....面对这一需求,我们当然也可以使用自己的双手进行编写,如果你时间充足可以试试. 今天我们讲解一下如何在angular框架 ...

  7. js获取宽高

    document.body.clientWidth ==> BODY对象宽度 document.body.clientHeight ==> BODY对象高度 document.docume ...

  8. hdu1150 Machine Schedule 经典二分匹配题目

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1150 很经典的二分题目 就是求最小点覆盖集 二分图最小点覆盖集=最大匹配数 代码: #include& ...

  9. 浅论Javascript在汽车信号测试中的应用

    起因 上周老板又给了我这个车辆工程毕业的码农一份工作: 要我写一个测试台架出来. 我先简单的分析了测试台架的几种典型的工况: 1.发送一个CAN信号,测试能否查到. 2.发送一个信号,是否能在规定时间 ...

  10. 每天一道Java题[2]

    问题 可以直接根据hashCode()方法产生的值判断两个对象是否相等吗? 解答 不能!根据Wikipedia(https://en.wikipedia.org/wiki/Java_hashCode( ...