前言
前面一篇文章对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