}
return cached;
}
这个方法和之前的 loadFromActiveResources差别不大,先看看 getEngineResourceFromCache(key):
// Engine.java
private final MemoryCache cache;
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key); // 1
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
result = (EngineResource<?>) cached;
} else {
result = new EngineResource<>(cached, true /isMemoryCacheable/, true /isRecyclable/);
}
return result;
}
这个方法就是从 MemoryCache中拿出一个EngineResource并返回。那我们要去看看 MemoryCache做了啥,但是MemoryCache是一个接口类,没有实现,在向前找的时候发现它是在创建 Glide initalizeGlide() 的过程中创建的,这里直接看创建的代码:
// GlideBuilder.java
@NonNull
Glide build(@NonNull Context context) {
…
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
…
}
// LruResourceCache.java
public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
…
}
LruResourceCache实现了MemoryCache,并继承了LruCache。
这里简单的描述一下LruCache,它是最近最少使用策略,实现原理是 accessOrder为true的 LinkedHashMap。所以在getEngineResourceFromCache的注释1中,它就是调用了 LinkedHashMap.remove(key)来看Map中是否有key对应的 EngineResources。
回到 loadFromCache中:
// Engine.java #loadFromCache
…
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
…
如果remove到的资源是不为null的,则调用 acquire()方法,并且调用 activeResources.activate(key, cached),我们看看这个方法做了什么:
// ActiveResources.java
void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key,
resource,
getReferenceQueue(),
isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
它把 key和EngineResource作为元素 put到了 ActiveResources的 HashMap中。便于以后 loadFromActiveResources去拿。
然后最后return为null。 loadFromCache()就结束了。可以看出来 LruCache也是在运行时产生的,所以它也是内存缓存。
这里出现了一个问题,这里出现了两个内存缓存,一个是HashMap缓存,一个是LruResourcesCache的缓存,在前者拿资源拿不到的情况下,去拿后者,如果后者拿到了,会把该资源放到前者中缓存。乍一看是没事找事,为什么不把所有的资源都放在一个cache下存储呢?这需要往后面的代码看。
接下来继续load()函数中:
private final Jobs jobs;
// Engine.java #load
…
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey(“Added to existing load”, startTime, key);
}
return new LoadStatus(cb, current);
}
…
通过 Jobs去拿一个 EngineJob,如果 EngineJob不为null,则调用其 addCallback(),这个方法最终也会调用 onResourceReady(),并返回一个 LoadStatus 。先来看看 jobs的get方法:
// Jobs.java
private final Map<Key, EngineJob<?>> jobs = new HashMap<>();
private final Map<Key, EngineJob<?>> onlyCacheJobs = new HashMap<>();
EngineJob<?> get(Key key, boolean onlyRetrieveFromCache) {
return getJobMap(onlyRetrieveFromCache).get(key);
}
private Map<Key, EngineJob<?>> getJobMap(boolean onlyRetrieveFromCache) {
return onlyRetrieveFromCache ? onlyCacheJobs : jobs;
}
onlyRetrieveFromCache这个字段中文意思为:是否只从Cache中搜索。默认情况下是 false。它就会去 jobs这个HashMap中拿EngineJob。这里也是一个缓存,但他并不是之间缓存图片资源。
EngineJob它不是一个图片资源,那它是什么呢?这里还不是很清晰,先往load下面走,
// Engine.java #load
…
EngineJob engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
…
这里是load方法的最后一步,在上述三种缓存都命中不到资源的情况下,会创建一个 EngineJob和一个 DecodeJob,将 key和engineJob一起绑定加入到 Jobs的HashMap,这样之后上面的jobs缓存就有资源可以找,然后调用 EngineJob.addCallback和 Engine.start()
这里的重点是 start方法,它是没有内存缓存后,去做的事情,所以这里算是一个新的开始。 也就是说,load可以看成两个部分,这里开始就是第二个部分。
// EngineJob.java
private final GlideExecutor diskCacheExecutor;
private final GlideExecutor sourceExecutor;
private final GlideExecutor sourceUnlimitedExecutor;
private final GlideExecutor animationExecutor;
public void start(DecodeJob decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
-
? diskCacheExecutor
- getActiveSourceExecutor();
executor.execute(decodeJob);
}
EngineJob里面有线程池执行器 GlideExecutor,它就是一个ExecutorService,因为加载图片是一个耗时操作,所以放到子线程做,所以这里出现了 Executor,其次,在EngineJob中,exeutor执行器并不是只有一个:
它会先根据缓存策略,拿到对应executor,比如这里的 :
// EngineJob.java
GlideExecutor executor = decodeJob.willDecodeFromCache()
-
? diskCacheExecutor
- getActiveSourceExecutor();
就会根据 “是否在Cache中解码” 这个条件来拿 disCacheExecutor,还是其他三个中的执行器。
在默认情况下, DiskCacheStategey是 AUTOMATIC的,这里就是true,即用的是 diskCacheExecutor。
接下来就会拿着这个线程执行器去执行 DecodeJob,就是调用它的 run():
// DecodeJob.java
@Override
public void run() {
…
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (Throwable t) {
…
} finally {
…
}
}
notifyFailed()是通知失败的方法,然后没取消,就调用 runWrapped()
// DecodeJob.java
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
这里根据 runReason分成了三种情况:
- INITIALIZE
在第一次我们提交执行任务的状态
- SWITCH_TO_SOURCE_SERVICE
当我们想要从 DiskCache 的做法切换到 Source执行的做法(这里我也不知道干嘛的)
- DECODE_DATA
该状态标识着:当我们在子线程中拿到了一个我们从未拥有过的资源,这个时候我们需要切换回主线程,去让主线程得到这个资源。
当然了,上面的三个是执行 DecodeJob的原因,它强调的是原因,DecodeJob又维护了一套当前的状态,和上面产生了联系:
- INITIALIZE
和上面初始时一样
- RESOURCE_CACHE
根据一个 缓存资源 进行 解码(Decode)
- DATA_CACHE
根据一个 缓存的源数据 进行 解码(Decode)
- SOURCE
根据 获取到的源数据 进行 解码
- ENCODE
在一个 资源成功加载后, 为了将它缓存起来,进行编码
- FINISHED
要结束了
假设我们是第一次进入到这个方法,那我们的runReason是 INITIALIZE,接下来会调用 getNextState(Stage.INITIALIZE):
// DecodeJob.java
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:
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
// 这里是默认情况下的 DiskCacheStaratey(磁盘)的缓存模式:
// DiskCacheStrategy。java
public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
@Override
public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
EncodeStrategy encodeStrategy) {
return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
|| dataSource == DataSource.LOCAL)
&& encodeStrategy == EncodeStrategy.TRANSFORMED;
}
@Override
public boolean decodeCachedResource() {
return true;
}
@Override
public boolean decodeCachedData() {
return true;
}
};
在 INITIALIZE状态下调用了 getNextStage(),state就变成了 Stage.RESOURCE_CACHE,接下来在 runWrapped中:
调用了 currentGenerator = 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);
}
}
返回了一个 ResourceCacheGenerator,它现在是做什么的还不知道。
继续往下走 ,会调用 runGenerators():
// DecodeJob.java
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) { // 1
stage = getNextStage(stage); // 2
currentGenerator = getNextGenerator(); // 3
if (stage == Stage.SOURCE) {
reschedule(); // 4
return;
}
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
这个方法会开启一个 while循环,判断条件中第三个较为关键:调用 currentGenerator.startNext()
如果这个方法的结果为false,则 stage进入到下一个状态, currentGenerator换成下一个执行器。
在当前状态为 Stage.SOURCE时,调用了 reschedule(),然后return,也就是说 SOURCE为当前decodeJob执行的最后一个状态。
如果要一直到最后 SOURCE,那么当前的DecodeJob会走这么个顺序:
(1)DecodeJob的状态:
RESOURCE_CACHE -> DATA_CACHE -> SOURCE
(2)currentGenrator的变化:
ResourceCacheGenerator -> DataCacheGenerator -> SourceGenerator
(3) startNext的执行
ResourceCacheGenerator.startNext() = false -> DataCacheGenerator .startNext() = false
这就是在 startNext()一直为false的情况下。 我们现在就去研究一下这个方法,由于 Generator是一个发生器,在这里分成了两个,所以startNext()有必要拆出来讲解。
startNext()这个方法有点长,并且借助了一个新的类 DecodeHelper的辅助,这个类主要做了组件注册,用来扩展或替换Glide的默认加载,还有负责解码、编码的Registry类。而且我们其实还并不是很清楚 ResourceCacheGenerator、DataCacheGenerator、SourceGenerator这些所关联 DataFetcher的类是做什么的,所以有必要先简单的介绍他们。
1.3 DataFetcher、ModelLoader以及DecodeHelper的介绍
1.关于DataFetcher
它是一个接口,先来看看它的定义:
// DataFetcher.java
public interface DataFetcher {
interface DataCallback {
void onDataReady(@Nullable T data); // 资源已经弄好了
void onLoadFailed(@NonNull Exception e); //加载失败回调
}
void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback); //加载方法
void cleanup(); //资源释放
void cancel(); // 取消加载
@NonNull
Class getDataClass();
@NonNull
DataSource getDataSource();
}
DataFetcher是Glide的数据加载模块,定义了数据的加载时遵循的框架。
它的使用大概就是:通过调用 loadData()来加载资源,当加载成功后,调用 onDataReady()进行回调。如果失败,调用 onLoadFailed()。非常好懂。
它有很多个实现类,比如:
- LocalUriFetcher
使用 ContentResolver 从本地资源的Uri加载数据
- AssetPathFetcher
使用 AssetManager从asset path中获取数据的抽象类
- HttpUrlFetcher
具体加载的数据类型为 InputStream,从网络Url中获取数据
…
2.关于ModelLoader
它也是个接口,来看看代码:
public interface ModelLoader<Model, Data> {
class LoadData {
public final Key sourceKey;
public final List alternateKeys;
public final DataFetcher fetcher;
public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher fetcher) {
this(sourceKey, Collections.emptyList(), fetcher);
}
public LoadData(@NonNull Key sourceKey, @NonNull List alternateKeys,
@NonNull DataFetcher fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
this.fetcher = Preconditions.checkNotNull(fetcher);
}
}
@Nullable
LoadData buildLoadData(@NonNull Model model, int width, int height,
@NonNull Options options);
boolean handles(@NonNull Model model);
}
它是一个工厂类的接口,用于将任意复杂的数据模型转换为具体的数据类型。
复杂的数据模型:通过给Glide设置了的各种属性,比如动画、缩放、模糊等等配置
具体的数据类型:LoadData ---- 由一系列的Key和DataFetcher组成的数据结构
它可以构建像 HttpGlideUrlLoader,内部构建一个HttpUrlFetcher,来进行网络的下载资源
也可以构建 FileLoader,内部构建一个 FileFetcher,来进行文件资源的获取。
它和DataFetcher是一起绑定的。
3. DecodeHelper
具体讲解需要费大量时间,所以可能会对本文出现岔道,所以具体请看这篇 DecodeHelper类相关方法分析
我们在 Glide.with().load(url) 的load中输入了获取图片资源的方式,这个方式可能是String、BitMap、Uri等。而 DecodeHelper的作用就是通过分析这个方式,分析出我们想要获取图片资源,需要哪些 ModelLoader、CacheKeys等。
比如当我们输入的是一个Url的字符串,它就能分析出我们所需的 cacheKey为:[GlideUrl,ObjectKey],从而获取到对应的LoadData
1.4 DataFetcherGenerator.startNext()
在1.2节中讲到,如果说一直要执行到 SOURCE状态,要先执行 ResourceCacheGenerator.startNext()和 DataCacheGenerator .startNext() = false ,这里看下两个方法的实现。
1.关于ResourceCacheGenerator的实现
// ResourceCacheGenerator.java
public boolean startNext() {
List sourceIds = helper.getCacheKeys(); // 1
if (sourceIds.isEmpty()) { // 2
return false;
}
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses(); // 3
if (resourceClasses.isEmpty()) {
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
}
while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
currentKey =
new ResourceCacheKey(
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions()); // 4
cacheFile = helper.getDiskCache().get(currentKey); // 5
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,
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。







既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)

最后
这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容

除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~
喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
628)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-i4IIdO0p-1712136289629)]
最后
这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容
[外链图片转存中…(img-oqP3u1L2-1712136289629)]
除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。
[外链图片转存中…(img-xQGE3RFt-1712136289630)]
分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~
喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
738

被折叠的 条评论
为什么被折叠?



