Glide图片加载过程(简)
调研版本为4.7.1
为了更加简单的理解,会将函数代码简化,详细代码请自行照源码对比
Glide用法
Glide.with(...).load(...).into(...)
我们从into方法开始探寻之旅
开启加载线程
RequestBuilder.class
public Target into() {
Request request = buildRequest(...);
requestTracker.runRequest(request);
}
requestTracker是RequestTracker对象
RequestTracker.class
public void runRequest(Request request) {
request.begin();
}
request是SingleRequest对象
SingleRequest.class
public void begin() {
onSizeReady(...);
}
public void onSizeReady(...) {
engine.load(...)
}
engine是Engine类的对象
Engine.class
void load(...) {
engineJob.start(...)
}
engineJob是EngineJob类的对象
EngineJob.class
public void start(DecodeJob job) {
GlideExecutor executor = ... //创建线程池
executor.execute(job); //job是一个runnable对象,此语句开启线程
}
获取资源过程
上面我们知道了开启线程的过程,这个线程主要就是获取二进制流并且编码成资源文件,如Bitmap、Drawable等用来在Android中显示。
DecodeJob.class
//线程从run方法开始
public void run() {
runWrapped();
}
private void runWrapped() {
runGenerators();
}
private void runGenerators() {
currentGenerator = getNextGenerator();
//依次调用generator的startNext方法
//startNext返回值表示是否可以获得图片的二进制流
//如果generator能获取图片数据,那么就不需要下级的generator去尝试获取图片数据
while (!currentGenerator.startNext()) {
...
currentGenerator = getNextGenerator();
}
}
//第一次调用该方法返回ResourceCacheGenerator
//第二次返回DataCacheGenerator
//第三次返回SourceGenerator(本地没有缓存时使用,下载数据并放入缓存)
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);
}
}
本例是一个imageView加载网络图,ResourceCacheGenerator的startNext为false,这样会调用DataCacheGenerator的startNext方法。
DataCacheGenerator.class
//startNext方法会通过循环所有的modelLoader分别调用buildLoadData方法
//如果所有modelLoader获取的loadData都为空那么就返回false
//这就意味着Glide将调用下一个Generator的startNext方法
//也就是SourceGenerator的startNext方法
public boolean startNext() {
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
File cacheFile = helper.getDiskCache().get(originalKey);
boolean started = false;
while (!started && hasNextModelLoader()) {
//我们自定义的ModelLoader就可以在这派上用场了
ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
ModelLoader.LoadData loadData = modelLoader.buildLoadData(...);
if (loadData != null) {
started = true;
//本例中ByteBufferFileLoader获取的loadData不为空
//本例中loadData.fetcher是ByteBufferFetcher的对象
loadData.fetcher.loadData(this);
}
}
return started;
}
ByteBufferFetcher.class
public void loadData(DataFetcher.DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
callback.onLoadFailed(e);
return;
}
callback.onDataReady(result);
}
这里的callback是DataCacheGenerator对象,该类实现了DataFetcher.DataCallback接口,所以此处回调到DataCacheGenerator类中的onDataReady方法
DataCacheGenerator.class
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
cb是DecodeJob类对象
DecodeJob.class
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
//将二进制流解码成资源文件
decodeFromRetrievedData();
}
}
private void decodeFromRetrievedData() {
Resource<R> resource = null;
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
}
}
notifyEncodeAndRelease方法里主要是使用Handler回到主线程调用各种回调以及释放资源。至此,加载过程就分析完毕啦。
在上面我们已经完整的走过了一个加载流程,但是Glide缓存的文件是什么形式的呢?如果一个图片的url被Glide加载缓存过,我们是不是可以通过该url获取缓存文件呢?获取的缓存文件是不是还是.jpg、.png等图片形式的可以直接从手机提取到电脑呢?如果你有这些疑问,那就继续往下看吧~
Glide缓存机制
Glide默认缓存在 data/data/包名/cache/image_manager_disk_cache 文件夹下,并且缓存文件的文件名根本不能直观上和请求的图片URL所匹配:
类似这种021db417987c5c446d2042037c78e9d9c127432d5a8cdd2b58cd2a702fc917b3.0,后缀还是 .0 。是不是有点头大,让我们去看看文件名的生成规则吧。

Glide使用DiskLruCacheWrapper类进行缓存文件操作,我们主要看put和get方法,一个用于取缓存,一个用于存缓存。
DiskLruCacheWrapper.class
public File get(Key key) {
String safeKey = safeKeyGenerator.getSafeKey(key);
File result = null;
final DiskLruCache.Value value = getDiskCache().get(safeKey);
result = value.getFile(0);
return result;
}
public void put(Key key, DiskCache.Writer writer) {
String safeKey = safeKeyGenerator.getSafeKey(key);
DiskLruCache diskCache = getDiskCache();
DiskLruCache.Editor editor = diskCache.edit(safeKey);
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
}
safeKeyGenerator是SafeKeyGenerator类的对象,其getSafeKey方法主要通过图片url获取缓存文件名,getSafeKey需要传入一个Key对象,Key对象就包括了要加载的url。
SafeKeyGenerator.class
public String getSafeKey(Key key) {
return calculateHexStringDigest(key);
}
private String calculateHexStringDigest(Key key) {
return Util.sha256BytesToHex(container.messageDigest.digest());
}
这里打个断点让大家看看Key里都有什么成员变量

calculateHexStringDigest方法也很简单,就是对该URL使用SHA256加密算法加密
为了让大家理解,这里贴两张图,第一张图是将图片地址字符串进行加密计算结果图,第二张图是手机缓存文件夹图片


那么后缀 .0 是怎么生成的呢?
我们可以看到在调用DiskLruCacheWrapper的put函数保存缓存文件时有一句代码DiskLruCache.Editor editor = diskCache.edit(safeKey);
其中diskCache是DiskLruCache对象
DiskLruCache.class
private DiskLruCache.Editor edit(String key) {
DiskLruCache.Entry entry = lruEntries.get(key);
if (entry == null) {
entry = new DiskLruCache.Entry(key);
lruEntries.put(key, entry);
}
}
注意了,这里的lruEntries是一个LinkedHashMap,里面存的就是图片url和DiskLruCache.Entry,其中DiskLruCache.Entry就存有缓存文件名!
可以看到Glide会优先从该Map中取,如果没有找到则自己创建DiskLruCache.Entry然后再放入Map
我们可以直接看DiskLruCache.Entry的构造函数
private Entry(String key) {
cleanFiles = new File[valueCount];
StringBuilder fileBuilder = new StringBuilder(key).append('.');
for (int i = 0; i < valueCount; i++) {
fileBuilder.append(i);
cleanFiles[i] = new File(directory, fileBuilder.toString());
}
}
这里cleanFiles就存放着缓存文件,其中valueCount=1,至于valueCount为什么是1是因为调用DiskLruCache的静态方法open public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) 时传的valueCount参数为1
那么既然valueCount为1,上面代码应该可以简化了吧?
private Entry(String key) {
cleanFiles = new File[1];
StringBuilder fileBuilder = new StringBuilder(key).append('.');
fileBuilder.append(0);
cleanFiles[0] = new File(directory, fileBuilder.toString());
}
所以看了这么多,想到怎么用url获取本地图片了嘛?
另外,试试把 xxx.0 换成 xxx.png ~
Glide图片加载过程(简)的更多相关文章
- Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能
我们的Glide系列文章终于要进入收尾篇了.从我开始写这个系列的第一篇文章时,我就知道这会是一个很长的系列,只是没有想到竟然会写这么久. 在前面的六篇文章中,我们对Glide的方方面面都进行了学习,包 ...
- Diycode开源项目 Glide图片加载分析
1.使用Glide前的准备 1.1.首先要build.gradle中添加 github原地址点击我. 参考博客:Glide-开始! 参考博客:android图片加载库Glide的使用介绍. 参考博 ...
- 深入探索Glide图片加载框架:做了哪些优化?如何管理生命周期?怎么做大图加载?
前言 Glide可以说是最常用的图片加载框架了,Glide链式调用使用方便,性能上也可以满足大多数场景的使用,Glide源码与原理也是面试中的常客. 但是Glide的源码内容比较多,想要学习它的源码往 ...
- android Glide图片加载框架的初探
一.Glide图片加载框架的简介 谷歌2014年开发者论坛会上介绍的图片加载框架,它让我们在处理不管是网路下载的图片还是本地的图片,减轻了很多工作量, 二.开发步骤: 1.添加链接库 compile ...
- Glide图片加载回调
1.方法一:设置图片中监听 使用的是SimpleTarget类,他继承自BaseTarget,需要重写onResourceReady方法,onResourceReady方法表示加载完成后的回调,下面列 ...
- android glide图片加载框架
项目地址: https://github.com/bumptech/glide Glide作为安卓开发常用的图片加载库,有许多实用而且强大的功能,那么,今天就来总结一番,这次把比较常见的都写出来,但并 ...
- 037 Android Glide图片加载开源框架使用
1.Glide简单介绍 Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式加载和展示图片.Glide是一个快速高效的Androi ...
- Glide图片加载库的使用
http://www.cnblogs.com/whoislcj/p/5558168.html这篇文章写得很详细,我这里简单说一些 1.app的build.gradle的dependencies里面添加 ...
- 122、详解Glide图片加载库常用方法
转载:http://blog.csdn.net/android_xiong_st/article/details/53129256 Glide加载网络图片, 显示的还是以前的图片! (最全解决方案!) ...
随机推荐
- 洛谷 P1120 小木棍 dfs+剪枝
Problem Description [题目链接] https://www.luogu.com.cn/problem/P1120 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不 ...
- 洛谷 P816 忠诚 题解
每日一题 day28 打卡 Analysis 这道题用线段树维护区间最小值很简单,因为没有修改所以连lazy_tag都不用,但是这道题可以用树状数组维护区间最小值,非常骚气. 线段树代码: #incl ...
- MySQL 是怎么保证数据一致性的(转载)
在<写数据库同时发mq消息事务一致性的一种解决方案>一文的方案中把分布式事务巧妙转成了数据库事务.我们都知道关系型数据库事务能保证数据一致性,那数据库到底是怎么设计事务这一特性的呢? 一. ...
- 搞清楚ourhdr.h是什么及运行第一个UNIX C程序
好多人开始学:UNIX 环境高级编程这本书时:看到书里面说的头文件ourhdr.h而找不到她在哪里:而且无法开始继续学习:其实这个就是系统的内核标准头文件: 而她的位置在:/usr/include/u ...
- 洛谷 P1456Monkey King
题目描述 要把打架的两堆猴子合并为一堆,查询的又是最大值,所以很容易想到可并堆. 题目要求打完架后战斗力最大的猴子的战斗力要减半,但不能直接在堆中进行这个操作,因为战斗力减半后这只猴子不一定是战斗力最 ...
- CODE FESTIVAL 2016 Grand Final 题解
传送门 越学觉得自己越蠢--这场除了\(A\)之外一道都不会-- \(A\) 贪心从左往右扫,能匹配就匹配就好了 //quming #include<bits/stdc++.h> #def ...
- 洛谷P3522 TEM-temperature
题目 单调队列+阅读理解 简化题意. 找到一个最长的区间使得区间每个点的r要大于该点之前的点的l. 然后可以用单调队列维护单调递减的l.最后尺取法O(n)枚举所有区间并取最大值. 单调队列可以快速找某 ...
- LeetCode之最大子段和
1.原问题 给定一个数组,求这个数组的连续子数组中,最大的那一段的和.如数组[-2,1,-3,4,-1,2,1,-5,4] 的子段为:[-2,1].[1,-3,4,-1].[4,-1,2,1].….[ ...
- 【洛谷】P2261 [CQOI2007]余数求和
题面?? 点我获得题面QAQ 我这个咕儿终于在csp初赛前夕开始学习数论了! 我是绝对不会承认之前不学数学是因为去年刚开始学OI的时候就跟yyq他们学莫比乌斯反演然后自闭的 分析 对于k mod i, ...
- mysql忘记密码恢复
MySQL忘记密码恢复密码的实现方法 作者:mdxy-dxy 流传较广的方法,mysql中文参考手册上的,各位vps主机租用客户和服务器托管用户忘记mysql5.1管理员密码时,可以使用这种方法破解下 ...