前言
前面一篇文章对Glide第一次加载网络图片的流程通过源码的方式一步步的带大家看了下,至少对Glide的整个框架有了一个大致的认识。这篇文章我们继续深入。
概要
这一篇主要介绍下,第一次加载图片缓存磁盘后,重新启动app,再次加载的时候,从磁盘读取的过程。中间还会涉及到Glide其他的一些源码内容。
正文
从磁盘加载文件,那么肯定就有把图片存起来的这个步骤。我们先来看看Glide是如何把图片存起来的。
上篇文章已经简单的介绍了,就是图片加载成功之后重新又调用了SourceGenerator的startNext方法。
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
...
}
重点就是看下cacheData这个方法,如何去缓存图片。上一篇文章,重在流程,所以一笔带过了,这次我们就继续深入。
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
...
}
其实最主要的就是这4行代码,我们一个个来解析。
- 获取编码器
<X> Encoder<X> getSourceEncoder(X data) throws Registry.NoSourceEncoderAvailableException {
return glideContext.getRegistry().getSourceEncoder(data);
}
...
public <X> Encoder<X> getSourceEncoder(@NonNull X data) throws NoSourceEncoderAvailableException {
Encoder<X> encoder = encoderRegistry.getEncoder((Class<X>) data.getClass());
if (encoder != null) {
return encoder;
}
throw new NoSourceEncoderAvailableException(data.getClass());
}
...
public synchronized <T> Encoder<T> getEncoder(@NonNull Class<T> dataClass) {
for (Entry<?> entry : encoders) {
if (entry.handles(dataClass)) {
return (Encoder<T>) entry.encoder;
}
}
return null;
}
一步步跟入,就是这3个方法,总结来说,就是一开始的时候在EncoderRegistry对象中注册多个编码器,通过key-value的形式保存起来,可以通过传入的数据类型来获取到自己对应的编码器。
这里就直接说下结果。前面网络请求返回对象是ContentLengthInputStream 类型的。

自己debug一下,就会比较清楚,encoders里面注册了2个Encoder,分别用来解析ByteBuffer和InputStream。
很显然,这里返回了StreamEncoder来处理。
创建
DataCacheWriter
把第一步获取到的StreamEncoder当作参数传入。只是创建个对象,没什么好说的。创建缓存的DataCacheKey
key-value的方式缓存数据,这里的key就是DataCacheKey。缓存数据
这里就是重点了,开始进行磁盘缓存数据。
helper.getDiskCache().put(originalKey, writer);
首先我们要知道helper.getDiskCache()获取到的对象是什么。因为三方框架中会使用很多接口,所以我们有时间直接看代码并不能马上知道具体对应是哪个实现。
这里有2个办法。
- debug跟入直接看
- 不停往前找,用
ctrl+左键一直往前找引用,找赋值的地方
这里我就不带大家这样一步步找了,直接看关键的几个地方。
DecodeHelper->diskCacheProvider=>
DecodeJob->diskCacheProvider=>
DecodeJobFactory->diskCacheProvider=>
Engine->diskCacheProvider这里就发现了
this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);
但是这里又有一个参数diskCacheFactory
我们还需要看这个是那里来的,继续往前找
Engine->diskCacheFactory=>
GlideBuilder->diskCacheFactory这里发现了
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
在构建Glide对象的时候如果没有传入diskCacheFactory,那么这里就会默认生成一个InternalCacheDiskCacheFactory。
好了这样我们就可以重新再来看下
helper.getDiskCache().put(originalKey, writer);
其实就是
helper.getDiskCache().put(originalKey, writer);
...
DiskCache getDiskCache() {
return diskCacheProvider.getDiskCache();
}
...
@Override
public DiskCache getDiskCache() {
if (diskCache == null) {
synchronized (this) {
if (diskCache == null) {
diskCache = factory.build();
}
if (diskCache == null) {
diskCache = new DiskCacheAdapter();
}
}
}
return diskCache;
}
前面我们已经一鼓作气,把factory也找出来了,也就是InternalCacheDiskCacheFactory。
public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory
//build方法在DiskLruCacheFactory里面
public DiskCache build() {
File cacheDir = cacheDirectoryGetter.getCacheDirectory();
if (cacheDir == null) {
return null;
}
if (!cacheDir.mkdirs() && (!cacheDir.exists() || !cacheDir.isDirectory())) {
return null;
}
return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
}
这里就是创建缓存的文件。
public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory {
public InternalCacheDiskCacheFactory(Context context) {
this(context, "image_manager_disk_cache", 262144000L);
}
public InternalCacheDiskCacheFactory(Context context, long diskCacheSize) {
this(context, "image_manager_disk_cache", diskCacheSize);
}
public InternalCacheDiskCacheFactory(final Context context, final String diskCacheName, long diskCacheSize) {
super(new CacheDirectoryGetter() {
public File getCacheDirectory() {
File cacheDirectory = context.getCacheDir();
if (cacheDirectory == null) {
return null;
} else {
return diskCacheName != null ? new File(cacheDirectory, diskCacheName) : cacheDirectory;
}
}
}, diskCacheSize);
}
}
在InternalCacheDiskCacheFactory可以看出图片缓存的文件路径。

就是在context.getCacheDir()里面的image_manager_disk_cache文件夹。
我们可以发现,我这个设备里面已经缓存了一个文件。
如果说,你不想存在这个文件,你就自定义一个DiskLruCacheFactory
在前面我们会发现最后在build方法中创建的是
return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
一个DiskLruCacheWrapper对象,所以前面缓存的时候也是调用了DiskLruCacheWrapper的put方法。
public void put(Key key, Writer writer) {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
...
DiskLruCache.Editor editor = diskCache.edit(safeKey);
...
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
...
} finally {
writeLocker.release(safeKey);
}
}
...
//writer.write
public boolean write(@NonNull File file) {
return encoder.encode(data, file, options);
}
DiskLrcCache具体的逻辑这里先不介绍,只要知道了是通过key-value缓存到了本地。后面可以直接通过key获取到缓存的数据。
接下来,我们尝试下,重启App,让Glide从磁盘加载图片。前面的步骤肯定都是一样的。我们就直接从DecodeJob的run方法开始看。
public void run() {
...
runWrapped();
...
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
...
}
}
private void runGenerators() {
...
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
...
}
之前是先在ResourceGenerator,DataCacheGenerator里面去找里面的loadData能不能处理这个请求。
在第一次加载网络图片的时候,前面2个都不能处理。但是经过了前面的磁盘缓存后。我们再进入DataCacheGenerator来看下里面的逻辑。
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
简单的来讲就是创建了一个DataCacheKey,然后helper.getDiskCache().get获取到缓存的数据。然后再交给对应的loadData去处理。
这里值得注意的是cacheKeys,modelLoaders
我们先来看cacheKeys,一步步往上找
DataCacheGenerator->cacheKeys=>
DecodeHelper->cacheKeys 最终是这里创建的,看下代码
List<Key> getCacheKeys() {
...
List<LoadData<?>> loadData = getLoadData();
for (int i = 0, size = loadData.size(); i < size; i++) {
LoadData<?> data = loadData.get(i);
if (!cacheKeys.contains(data.sourceKey)) {
cacheKeys.add(data.sourceKey);
}
...
}
}
}
return cacheKeys;
}
cacheKeys保存的其实是LoadData的sourceKey.
这里看下List<LoadData<?>>是如何获取到的
List<LoadData<?>> getLoadData() {
...
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
LoadData其实ModelLoader. buildLoadData生成的,所以,我们就继续往下看ModelLoader是哪里来的。
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//Registry.java
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
...
return result;
}
//ModelLoadRegistry.java
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
...
}
//ModelLoadRegistry.java
private synchronized <A> List<ModelLoader<A, ?>> getModelLoadersForClass(
@NonNull Class<A> modelClass) {
List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
if (loaders == null) {
loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
cache.put(modelClass, loaders);
}
return loaders;
}
//MultiModelLoaderFactory.java
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
if (alreadyUsedEntries.contains(entry)) {
continue;
}
if (entry.handles(modelClass)) {
alreadyUsedEntries.add(entry);
loaders.add(this.<Model, Object>build(entry));
alreadyUsedEntries.remove(entry);
}
}
return loaders;
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
路径很深,大家可以直接看MultiModelLoaderFactory.build方法.
值得注意的是这里有个变量是entries,我们看看下是什么东西。
private final List<Entry<?, ?>> entries = new ArrayList<>();
我们使用ctrl+鼠标左键找下引用的地方,看看哪里往里面添加东西了,添加了什么东西。
private <Model, Data> void add(
@NonNull Class<Model> modelClass,
@NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory,
boolean append) {
Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
entries.add(append ? entries.size() : 0, entry);
}
再ctrl+鼠标左键,看哪里调用了add,一步步往上找。
//MultiModelLoaderFactory.java
synchronized <Model, Data> void append(
@NonNull Class<Model> modelClass,
@NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
add(modelClass, dataClass, factory, /*append=*/ true);
}
//ModelLoaderRegistry.java
public synchronized <Model, Data> void append(
@NonNull Class<Model> modelClass,
@NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
multiModelLoaderFactory.append(modelClass, dataClass, factory);
cache.clear();
}
//Registry.java
public <Model, Data> Registry append(
@NonNull Class<Model> modelClass, @NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
最终找到,调用的地方是Glide的构造方法。

由于方法实在是太长了,这里就直接贴出图片。

我们再来看下方法,注意下参数名。
public <Model, Data> Registry append(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
modelClass->传入的数据class,这里我们传入的是http字符串,也就是String.class
dataClass->处理后得到的数据class,前面文章介绍了第一次加载图片得到的其实是InputStream的子类
factory->创建处理传入数据是modelClass这种类型,得到数据是dataClass这种类型的处理器的工厂
我们继续回过头看下
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
...
if (alreadyUsedEntries.contains(entry)) {
continue;
}
if (entry.handles(modelClass)) {
alreadyUsedEntries.add(entry);
loaders.add(this.<Model, Object>build(entry));
alreadyUsedEntries.remove(entry);
}
}
return loaders;
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
这里就看起来清晰多了,先调用entry.handles(modelClass)看看这个entry能不能处理modelClass这个类型的请求,如果可以就调用build方法构建一个ModelLoader
我们一步步来,先看handles方法
public boolean handles(@NonNull Class<?> modelClass) {
return this.modelClass.isAssignableFrom(modelClass);
}
非常简单,就看modelClass是不是之前注册的modelClass的子类。
那么继续看build方法。
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}
其实就是调用entry.factory.build(this)。前面已经介绍过了,factory其实就是前面注册的第三个参数。那么我们就可以看看之前,modelClass为String的对应的几个ModelLoaderFactory
根据前面的介绍是在Glide构造函数中添加的。我们就看下具体是哪几个。
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
所以得到的loaders就是4个上面的factory调用build之后所创建的ModelLoader。这里就以StringLoader.StreamFactory.build方法为例子介绍下.
public ModelLoader<String, InputStream> build(
@NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
这里就有个比较巧妙的地方,继续调用了multiFactory也就是MultiModelLoaderFactory的build方法,但是这次和前面不一样,这里传了2个参数,Uri.class对应modelClass,InputStream对应dataClass,举一反三。前面已经介绍了传了一个参数,就是找到前面注册的modelClass为String.class的factory。这里也是一样,只不过这里要找到modelClass为Uri.classs,dataClass为InputStream的factory。然后使用factory构建出对应的ModelLoader。
这里我们只需要知道,前面创建的StringLoader,内部有一个参数是uriLoader,而这个uriLoader就是处理modelClass为Uri,dataClass为InputStream的ModelLoader。那么我们可以在Glide的构造方法内找一下对应的factory。
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(Uri.class,
InputStream.class,
new UriLoader.StreamFactory(contentResolver))
...
有点多,我就不一一写出了。
接下来就往前面看。
//ModelLoaderRegistry.java
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
//这里获取到了4个
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
int size = modelLoaders.size();
boolean isEmpty = true;
List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
//这里过滤掉了一个
if (loader.handles(model)) {
if (isEmpty) {
filteredLoaders = new ArrayList<>(size - i);
isEmpty = false;
}
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}
大家可以注意下上面的注释,就是loader.handlers过滤了一个ModelLoader,这里就直接说了,过滤了DataUrlLoader
public boolean handles(@NonNull Model model) {
return model.toString().startsWith(DATA_SCHEME_IMAGE);
}
显然我们传入的字符串不是以这个为开头,所以为false。
所以最后传回去的是3个ModelLoader。继续往前看。
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//这边就是前面得到的3个ModelLoader
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
前面其实已经介绍过了,返回的其实是3个StringLoader,只不过是里面的uriLoader不太一样罢了。
public LoadData<Data> buildLoadData(@NonNull String model, int width, int height,
@NonNull Options options) {
Uri uri = parseUri(model);
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
...
//MultiModelLoader.java
public boolean handles(@NonNull Model model) {
for (ModelLoader<Model, Data> modelLoader : modelLoaders) {
if (modelLoader.handles(model)) {
return true;
}
}
return false;
}
uriLoader是一个MultiModelLoader,其实也是遍历一下,看看MultiModelLoader内部的ModelLoader能不能处理。
//HttpUriLoader
private static final Set<String> SCHEMES =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));
public boolean handles(@NonNull Uri model) {
return SCHEMES.contains(model.getScheme());
}
找到HttpUriLoader能够处理。然后调用HttpUriLoader.buildLoaderData
public LoadData<InputStream> buildLoadData(@NonNull Uri model, int width, int height,
@NonNull Options options) {
return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);
}
这里很奇怪,这里又来了一个urlLoader是什么东西。
创建HttpUriLoader的时候是根据一个factory创建的,
public static class Factory implements ModelLoaderFactory<Uri, InputStream> {
...
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
}
...
}
这段代码感觉又非常熟悉,跟前面很像,只不过这里的modelClass是GlideUrl,dataClass是InputStream,我们在Glide构造方法里面找一下
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
就这一个。
所以刚才urlLoader其实就是由HttpGlideUrlLoader.Factory()构建的HttpGlideUrlLoader。那么我们来看下HttpGlideUrlLoader.buildLoadData
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
GlideUrl url = model;
...
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
HttpUrlFetcher其实就是真正发起http请求获取数据的fetcher。这里就不在深入了。
这里我们重新再看下这行代码
urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options)
我们可以看下GlideUrl
public class GlideUrl implements Key
它实现了Key接口。所以前面获取DiskCacheKey传入的参数其实就是GlideUrl。那么我们就重新再回到最前面。DataCacheGenerator
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
...
Key sourceId = cacheKeys.get(sourceIdIndex);
...
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
经过前面介绍如何获取到ModelLoader已经Key是什么之后,再来看下这段代码,就会发现有不太一样的认识。
cacheFile这里已经不为null。
然后继续往下,从helper.getModelLoaders(cacheFile),其实就是找到modelClass为 File的factory。
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
.append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
然后反正就根据我刚才那样一步步往下走就好了,就会找到对应的ModelLoader然后生成对应的LoadData,这里就直接不再跟入了,
这里LoadData其实是ByteBufferFileLoader
private static final class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
...
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
...
result = ByteBufferUtil.fromFile(file);
...
callback.onDataReady(result);
}
里面的fetcher就是ByteBufferFetcher,然后调用loadData方法读取到数据。
总结
这篇文章主要是对磁盘缓存数据还有获取数据的分析,以及ModelLoader的分析。后续还会继续深入分析Glide