Glide v4 缓存解析,小白勿进

第一步:生成缓存的key,这里会根据上面所说的,模型,签名,长宽,变换以及原始资源,变换后的资源,以及选项创建一个key。这个是原始的key,后续如果执行到磁盘缓存会根据原生key生成新的key。 第二步:从内存中尝试获取资源文件,内存中分为两部分:活动资源以及内存缓存资源。

@Nullable
private EngineResource<?> loadFromMemory( EngineKey key, boolean isMemoryCacheable, long startTime) { if (!isMemoryCacheable) { return null; } //活动资源 EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey(“Loaded resource from active resources”, startTime, key);
}
return active;
}
//内存缓存资源
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
return cached;
}

return null;
}

活动资源 活动资源最终实现是通过ActiveResources。内部维护了一个Map<Key, ResourceWeakReference> ,也就是一个弱引用的hashMap,将活动资源存储在hashmap的弱引用中。

@VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();

synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}

内存缓存

private EngineResource<?> getEngineResourceFromCache(Key key) { Resource<?> cached = cache.remove(key);

final EngineResource<?> result; if (cached == null) { result = null; } else if (cached instanceof EngineResource) { // Save an object allocation if we've cached an EngineResource (the typical case). result = (EngineResource<?>) cached;
} else {
result =
new EngineResource<>(
cached, /isMemoryCacheable=/ true, /isRecyclable=/ true, key, /listener=/ this);
}
return result;
}

其中关键实现为MemoryCache 定义的cache,MemoryCache 的具体实现类为LruResourceCache。LruResourceCache 集成了LruCache,就是实现了LRU算法来维护内存缓存数据。

第三步,开启一个新的任务:从磁盘中读取或者从网络中读取需要的资源。 通过waitForExistingOrStartNewJob 进入 engineJob.start(decodeJob); decodeJob 实现了runnable接口,engineJob实现内部通过线程池去执行decodeJob 看下DecodeJob 的run -> runWrapped -> runGenerators

private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();

if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We’ve run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}

其中currentGenerator.startNext() 方法为真正执行加载,我们来看下currentGenerator 有哪些具体实现类。

DataFetcherGenerator实现类.png

下面为如果根据缓存策略返回当前的阶段,后面会根据当前阶段生成对应的生成器。

private Stage getNextStage(Stage current) {
switch (current) {
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;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}

三种生产器会根据当前的阶段进行创建

  • (变换后的)资源缓存文件对应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);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}

ResourceCacheGenerator

public boolean startNext() {
···省略代码
currentKey =
new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
// 根据硬盘缓存判断是否存在,具体实现为DiskLruCache
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
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;
}

这里会先从DiskCache尝试获取,默认的实现是DiskLruCache,在这里进行读取,然后在资源编码完成之后,存入磁盘。

DataCacheGenerator

DataCacheGenerator.startNext的逻辑和ResourceCacheGenerator基本相同。 第一步,创建DataCacheKey,这里和ResourceCacheGenerator生成Key方式不太一致。 第二步,尝试通过DiskCache获取元数据缓存 第三方,根据modelLoader加载数据

SourceGenerator

开始尝试去加载源数据,主要通过DataFetcher实现,DataFetcher会根据数据源类型进行选择合适的进行调用。

private void startNextLoad(final LoadData<?> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}

@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}

DataFetcher 实现类如下图所示。

DataFetcher 实现类

我们进入HttpUrlFetcher 看下具体实现,这里我们看到loadData方法内部的loadDataWithRedirects方法进入真正的文件网络数据读取。

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);
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField(“Location”);
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException(“Received empty or null redirect url”);
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

建议

当我们出去找工作,或者准备找工作的时候,我们一定要想,我面试的目标是什么,我自己的技术栈有哪些,近期能掌握的有哪些,我的哪些短板 ,列出来,有计划的去完成,别看前两天掘金一些大佬在驳来驳去 ,他们的观点是他们的,不要因为他们的观点,膨胀了自己,影响自己的学习节奏。基础很大程度决定你自己技术层次的厚度,你再熟练框架也好,也会比你便宜的,性价比高的替代,很现实的问题但也要有危机意识,当我们年级大了,有哪些亮点,与比我们经历更旺盛的年轻小工程师,竞争。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!!!!!!!

  • 准备想说怎么样写简历,想象算了,我觉得,技术就是你最好的简历

  • 我希望每一个努力生活的it工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

  • 有什么问题想交流,欢迎给我私信,欢迎评论

【附】相关架构及资料

Android高级技术大纲

面试资料整理

资料领取

点击这里免费获取

内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术

构及资料**

[外链图片转存中…(img-uIXVUqqJ-1711184908884)]

[外链图片转存中…(img-w3vcN9dV-1711184908884)]

资料领取

点击这里免费获取

内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值