总述
接着上面文章继续分析磁盘缓存.首先在上一篇文章我们知道了缓存类是在什么地方创建的,那么我就依此为线索,分析其最终在哪里调用了.因为磁盘缓存经过多次参数传递,不太好发现,所以先通过其构建去寻找,这也是阅读源码中的一个思路吧.
磁盘缓存类分析
GliderBuilder创建
- diskCacheFactory继承DiskLruCacheFactory
- 其内部重写了缓存目录创建方法
- DiskLruCache类相关的缓存原理,网上文章比较多,有兴趣可搜索查看
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
传递给Engine
engine = new Engine(memoryCache,diskCacheFactory,...);
通过LazyDiskCacheProvider包裹
- 后面创建DiskLruCache的工厂类变成了diskCacheProvider
//Engine的构造方法
this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);
传递到DecodeJobFactory
- DecodeJob是负责数据加载处理的核心类
- DecodeJobFactory是Engine的静态内部类
//Engine的构造方法
if (decodeJobFactory == null) {
decodeJobFactory = new DecodeJobFactory(diskCacheProvider);
}
创建DecodeJob时,设置diskCacheProvider
- DecodeJob根据流程图,其是在Engine.load中创建
- DecodeJob有DecodeJobFactory.build创建
DecodeJob<R> decodeJob =decodeJobFactory.build();
- build方法,通过Pools去获取DecodeJob实例
- 我们从这里可以学到一种构建对象的方法
build(...){
DecodeJob<R> result = Preconditions.checkNotNull((DecodeJob<R>) pool.acquire());
}
final Pools.Pool<DecodeJob<?>> pool =
FactoryPools.threadSafe(
JOB_POOL_SIZE,
new FactoryPools.Factory<DecodeJob<?>>() {
@Override
public DecodeJob<?> create() {
return new DecodeJob<>(diskCacheProvider, pool);
}
});
缓存添加方法的调用方法
-上面包裹类LazyDiskCacheProvider为DecodeJob.DiskCacheProvider接口实现类
- 我们将其传递给给了DecodeJob
- LazyDiskCacheProvider重写的接口方法会通过GlideBuild传递的diskCacheFactory获得缓存类DiskCache
private static class LazyDiskCacheProvider implements DecodeJob.DiskCacheProvider
interface DiskCacheProvider {
DiskCache getDiskCache();
}
public DiskCache getDiskCache() {
if (diskCache == null) {
synchronized (this) {
if (diskCache == null) {
diskCache = factory.build();
}
if (diskCache == null) {
diskCache = new DiskCacheAdapter();
}
}
}
return diskCache;
}
}
- 添加缓存方法put的调用
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}
在这里着里我们知道了磁盘缓存的添加是在DecodeJob的encode方法,我们记住这个方法,那它是在何时调用的呢,我们接下来回到数据的加载过程,开始分析,同时也能找到磁盘缓存的读取.
磁盘缓存的读取
通过上一篇的流程分析,我们知道数据的加载最终有DecodeJob中的三个核心方法处理:getNextStage;getNextGenerator();runGenerators
- getNextStage,会根据当前不同的状态,前往下一级状态,逐级处理,这就是磁盘缓存策略配置的控制方法,默认是初始化状态,我们默认配置了磁盘缓存,且是DataChache
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
- getNextGenerator,会根据不同的状态,创建相应的类
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);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
- runGenerators,内部会不断循环,调用getNextState,getNextGenerator,直到相应处理类返回正确的数据,当最后是Stage.Source时,回到EngineJob,然后再次调用DecodeJob,此时状态直接变成了SWITCH_TO_SOURCE_SERVICE,表示没有获取到缓存,调用SourceGenerator.statrNext方法获取数据.
- 我们这里分析磁盘缓存,就不展开,后面分析写入展开,主要分析DataCacheGenerator,进入其startNext方法;
- startNext,首先生成相应的key,然后调用LoadData.fetch.loadData
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey)
cacheFile = helper.getDiskCache().get(originalKey);
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);
}
- 在上一篇我们分析了Glide的构造方法,说明其在里面注册了各种类型的loader,其获取过程比较繁琐,我们直接相应的类查看,磁盘缓存,显然是FileLoader
- FileLoader的buildData方法返回FileFetcher,其是静态内部类
@Override
public LoadData<Data> buildLoadData(
@NonNull File model, int width, int height, @NonNull Options options) {
return new LoadData<>(new ObjectKey(model), new FileFetcher<>(model, fileOpener));
}
- FileFecher类的loadData方法,根据得到的cacheFile,获取数据
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
try {
data = opener.open(file);
} catch (FileNotFoundException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to open file", e);
}
callback.onLoadFailed(e);
return;
}
callback.onDataReady(data);
}
- 接下来就是依次回调,与网络请求回调逻辑大致一致,大家可根据流程图查看,最终设置给imageView,后面分析写入也会展开
到这里,磁盘缓存的读取完成,发现其有两种类型磁盘缓存.一种是Resource,一种是Data.具体区别看类的注释.代码注释和命名对可读性帮助很大呀
/**
* Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from cache files
* containing downsampled/transformed resource data.
*/
class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object>
/**
* Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from cache files
* containing original unmodified source data.
*/
class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object>
磁盘的写入
假设第一次调用,磁盘缓存没有,通过上面的分析,知道会调用SourceGenerator的startNext方法.在里面查找loadData,然后调用loadData.fetch.loadData
- 这里loadData是什么,fetch是那个类,我们在上篇文章中Glide的构造方法提及了,下面就逐步分析一下.
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
Loader的添加和查找
loader的添加
- Glide的构造方法中通过regeister.append添加
- 其会传递三个参数,第一个Model.class,第二个数据类型类,第三个工厂类
registry.append(ByteBuffer.class, new ByteBufferEncoder())
....
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
- regeister.append添加方法调用ModelLoaderRegister.append方法
@NonNull
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;
}
- 在ModelLoaderRegister.append方法调用MultiModelLoaderFactory的append方法
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);
}
- MultiModelLoaderFactory的添加方法会将其添加到Entry中
- ==Entry有三个变量,就是我们在Glide传递进去的参数==
- 到这里添加就完成了
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);
}
loader的查找
我们获取网络图片,传递的是字符串类型,数据类型是流
- sourcegenerator.startNext查找,采取的是遍历的方法,发现不符合条件,会递增,再去获取,我们记住这里的判断条件,后面拆开分析
- helper.getLoadData()调用register.getModelLoader方法,获取modelLoaders,然后遍历调用每一个loader的buildLoadData方法
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
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;
}
- register的getModelLoaders会调用ModelLoaderRegister.getModelLoaders方法,采用委托设计模式,register的添加和获取都是通过ModelLoaderRegister
- ModelLoaderRegister.getModelLoaders内部调用getModelLoadersForClass
@NonNull
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
if (result.isEmpty()) {
throw new NoModelLoaderAvailableException(model);
}
return result;
}
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
····
}
- getModelLoadersForClass内部会调用MultiModelLoaderFactory.build方法,里面会传入model类型,其通过DecodeHeelper传入,而DecodeHelper在DecodeJob构造时,创建,传入.那我们知道其就是String.class
- 在MultiModelLoaderFactory.build方法会调用entry.build方法
- 我们刚才分析添加时,就是添加进里面了,因为model.class是string.class有多个,接下里我们进入StringLoader类
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
if (entry.handles(modelClass)) {
alreadyUsedEntries.add(entry);
//这里是重点
loaders.add(this.<Model, Object>build(entry));
alreadyUsedEntries.remove(entry);
}
}
return loaders;
}
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}
- string.class利用有多个Factory,如果是网络请求,显然是StreamFactory,
- 里面又回调用Glide构造方法添加的modle为Url.class的工厂方法,在构造方法中我们发现了HttpUrlLoader.Factory;那我们看其build方法返回的loader
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));//里面的方法调用更上面的一样,就不分析,直接分析factory.build方法
}
public StringLoader(ModelLoader<Uri, Data> uriLoader) {
this.uriLoader = uriLoader;
}
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
}
- HttpUrlLoader返回的里面跟StringLoader一样,又套了一层,GlideUrl.class,这是4.10进行修改了,添加进去的.绕的有点迷糊,我们不再分析,直接查找GlideUrl.class添加时的Factory
append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
- 在HttpGlideUrlLoader里我们找到了Fetcher
@Override
public LoadData<InputStream> buildLoadData(
@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
- 在Fecher里面我们发现了网络请求,和SourceGenerator判断方法参数的实现.
public Class<InputStream> getDataClass() {
return InputStream.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
/** Indicates data was retrieved from a remote source other than the device. */
REMOTE,
- 显然我们是获取网络数据,其符合条件
- 然后调用HttpUrlFetch的loadData方法,内部会调用loadDataWithRedirects,获取网络流
loadData{
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
}
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true)
}
到这里我们分析了Loader的查找,然后找到了网络请求的处理类,然后就是依次回调,回调流程,可以查看流程图,其中有个步骤,就是回调DecodeJob.onDataFetcherReady.接下来就是查找我们一开始通过磁盘缓存类的传递,最终到了DecodeJob.encode方法,我们看DecodeJob.onDataFetcherReady是如何逐步调用到该方法
磁盘缓存写入方法的调用
- 网络请求成功返回数据后,会进行解码工作,通过decodeFromRetrievedData实现
- decodeFromRetrievedData调用decodeFromData完成解码,
- 然后调用notifyEncodeAndRelease
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
...
decodeFromRetrievedData();
...
}
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
}
...
}
- notifyEncodeAndRelease里面又两个方法,一个负责回调,返回EngineJob,一个就是磁盘缓存写入
- notifyComplete负责回调到EngineJob
- deferredEncodeManager.encode负责磁盘缓存写入,这里我们发现了diskCacheProvider,options
- deferredEncodeManager.encode负责添加缓存
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
Resource<R> result = resource;
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
//这里是关键方法
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}
到此通过简单调用分析大致流程,绘制流程图,然后分析缓存策略,逐步分析图片加载过程的每一个步骤.通过源码阅读发现,Gldie在每次升级会修改部分逻辑,但大致逻辑没有修改,其核心思想都是一致的.同时也可以观察到其覆盖了多种情况,一个简单的调用里面涉及了如此多的逻辑.设计的非常好.