写在之前

上一篇博文写的是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. IBM GPFS并行文件系统

    原文地址:http://www.hqschina.com/Show.aspx?info_lb=283&info_id=751&flag=103 IBM GPFS文件系统是一种专门为群集 ...

  2. python特征提取——pyAudioAnalysis工具包

    作者:桂. 时间:2017-05-04  18:31:09 链接:http://www.cnblogs.com/xingshansi/p/6806637.html 前言 语音识别等应用离不开音频特征的 ...

  3. ListView在异步加载动态图片时,往往最后一项或几项被遮盖(IM场景居多)

    如果ListView中得默认图片比较小,新图片加载后,撑大ListView中的对应项,导致最后一项或几项被覆盖. 解决思路: 1.默认图片设定和新图大小一样,换句话说,新图加载后转成和默认图片一样的大 ...

  4. iOS 发布证书提示 此证书的签发者无效 解决办法

    1. 打开钥匙串  查看发布证书 都是提示 此证书的签发者无效   解决办法 : 2. 到了 第 4 步骤 再去 查看 发布证书 就会 显示  此证书有效 3.  如果还不可以 就 把 Apple W ...

  5. “永恒之蓝"漏洞的紧急应对--毕业生必看

    早上6点多起床了,第一次起这么早,昨天晚上12点多,看到了一则紧急通知,勒索软件通过微软"永恒之蓝"漏洞针对教育网进行了大规模的攻击,而且有很多同学中招.中招后的结果如下图所示. ...

  6. springcloud(二):注册中心Eureka

    Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现.也是springcloud体系中最重要最核心的组 ...

  7. python requests 模拟登陆网站,抓取数据

    抓取页面数据的时候,有时候我们需要登陆才可以获取页面资源,那么我们需要登陆以后才可以跳转到对应的资源页面,那么我们需要通过模拟登陆,登陆成功以后再次去抓取对应的数据. 首先我们需要通过手动方式来登陆一 ...

  8. GO的初始简书(二)环境变量设置与说明

    安装GO后你应该做的一些事~ 将自己需要开发的项目加入环境变量中的gopath中 GOPATH其实就一个工作目录   -----正在开发的项目 1首先在本地新建目录 go_work 2 vi ~/.b ...

  9. 【http】http的方法,状态码和组成部分

    Http(Hypertext Transfer Protocol) HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传 ...

  10. idea 控制台输出 中文乱码 解决方法

    使用intellij idea 14.1时,console 会输出中文乱码.下面分两种情况解决这种问题:一种是maven构建项目.一种是tomcat(不以maven构建)构建项目. 1.tomcat输 ...