写在之前

上一篇博文写的是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. php操作memcache缓存基本方法

    memcache 是一个高效的分布式的内存对象缓存系统,他可以支持把php的各种数据(数组,对象,基本数据类型)放在它管理的内存中 1.代码使用 <?php //连接 $mem = new Me ...

  2. Python、PyCharm的安装及使用方法(Mac版)

    上周跟朋友喝咖啡时聊起我想学Python,她恰好也有这个打算,顺便推荐了一本书<编程小白的第1本Python入门书>,我推送到Kindle后,随手翻看了下,用语平实,简洁易懂. 之前在R语 ...

  3. servlet研究学习总结--OutputStream和PrintWriter的区别

    当用户和浏览器其进行交互时,会给服务器发送http请求,Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象.requ ...

  4. [Git]05 如何使用分支

     作者:Younger Liu, 本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可. 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发 ...

  5. 线段树区间更新操作及Lazy思想(详解)

    此题题意很好懂:  给你N个数,Q个操作,操作有两种,‘Q a b ’是询问a~b这段数的和,‘C a b c’是把a~b这段数都加上c. 需要用到线段树的,update:成段增减,query:区间求 ...

  6. apache配置多个站点

    序:这次项目主要是为了给微信客户端添加一个地址,在微信公众号里面添加一个可以访问的app下载页面,说起来很简单,但总不能为了这么小的一个网站新建一个web服务器吧! 现在开始配置,首先必须确认已经在L ...

  7. smart beta

    本文来至人大经济论坛,http://bbs.pinggu.org/thread-3151691-1-1.html 众所周知,beta在CAPM模型中衡量了相对于持有整个市场所带来的风险溢价(risk ...

  8. STL容器之优先队列(转)

    STL容器之优先队列 原地址:http://www.cnblogs.com/summerRQ/articles/2470130.html 优先级队列,以前刷题的时候用的比较熟,现在竟然我只能记得它的关 ...

  9. Transform java future into completable future 【将 future 转成 completable future】

    Future is introduced in JDK 1.5 by Doug Lea to represent "the result of an asynchronous computa ...

  10. jdbc连接数据库工具包模板

    jdbc连接数据库操作 jdbc连接数据库模板,收藏可做模板使用(小型工程,一般大工程都会用框架,c3p0等连接,不考虑此种方法!). 配置文件的使用(使用配置文件可以使我们后期的修改更加方便,当然, ...