Glide load源码、缓存机制分析

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.addCallbackEngine.start()

这里的重点是 start方法,它是没有内存缓存后,去做的事情,所以这里算是一个新的开始。 也就是说,load可以看成两个部分,这里开始就是第二个部分。

1.2 EngineJob的start方法解析


// 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,

helper.getWidth(), helper.getHeight(), helper.getOptions());

if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {

started = true;

loadData.fetcher.loadData(helper.getPriority(), this);

}

}

return started;

}

有了 1.3节中的介绍,我们可以来解析上面的源码:

注释1:根据model(即我们在Glide中传入的图片资源获取方式),可以拿到 cacheKeys,当我们传入的是Stirng类型的(比如Url),那么这个 cacheKeys就有两个元素 [GlideUrl,ObjectKey]

注释2:如果拿到的cacheKeys为空,则直接返回false

注释3:获取resourceClass的信息,就是glide所支持的资源类的信息,比如Bitmap

注释4:进行循环,根据注释1中的 Key和注释3中的 资源类信息,生成一个缓存的 key对象 ---- ResourceCacheKey

注释5: 拿着这个key去 DiskCache中寻找是否有缓存对象。如果查找成功,获取 它的 ModelLoader对象,失败则继续循环。

这个while循环就是为了找到对应的 ModelLoader信息,如果找不到则返回false,如果找到了则进入下一个while循环,这个循环中就是根据上一个循环找到的ModelLoader数组,遍历每个 ModelLoader,然后执行其 fetcher的 loadData()方法进行资源的加载。然后返回true。

ResourceCacheGenerator.startNext()这个方法总结为一句话就是: 根据ResourceCacheKey从磁盘缓存中获取缓存

如果获取不到,则执行DataCacheGenerator .startNext()

2.关于DataCacheGenerator的实现

// DataCacheGenerator.java

public boolean startNext() {

while (modelLoaders == null || !hasNextModelLoader()) {

sourceIdIndex++;

if (sourceIdIndex >= cacheKeys.size()) {

return false;

}

Key sourceId = cacheKeys.get(sourceIdIndex);

@SuppressWarnings(“PMD.AvoidInstantiatingObjectsInLoops”)

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;

}

这个方法和上述的 ResourceCacheGenerator中的startNext()很像,其实很多部分都是一样的, 但是这里有唯一一处不同是,就是去DiskCache中寻找用缓存用的Key并不是 ResourceCacheKey,而是一个 DataCacheKey

他们的区别是:

  • ResourceCacheKey

指向的是 包含缩减采样/转换资源数据的缓存文件

  • DataCacheKey

指向的是 包含原始未修改源数据的缓存文件

这也就能看出DataFetcherGenerator的作用是:

  • ResourceCacheGenerator

DataFetcherGenerator实现类,从包含缩减采样/转换资源数据的缓存文件生成DataFetchers

  • DataCacheGenerator

DataFetcherGenerator实现类,从包含原始未修改源数据的缓存文件生成DataFetchers

在1.2节中,这两个方法执行完之后,stage来到了 SWITCH_TO_SOURCE_SERVICE状态,那么他会runReason为调用 reschedule(),拿着非diskCacheExecutor的线程执行器再去执行一遍任务。

也就是重新执行了 DecodeJob.runWrapped(),而这个方法最终为调用 SourceGenerator.startNext()

3.关于SourceGenerator的实现

// SourceGenerator.java

@Override

public boolean startNext() {

if (dataToCache != null) {

Object data = dataToCache;

dataToCache = null;

cacheData(data); // 1

}

if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {

return true;

}

sourceCacheGenerator = null;

loadData = null;

boolean started = false;

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); // 2

}

}

return started;

}

// 将数据缓存到Disk中

private void cacheData(Object dataToCache) {

long startTime = LogTime.getLogTime();

try {

Encoder encoder = helper.getSourceEncoder(dataToCache);

DataCacheWriter writer =

new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());

originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());

helper.getDiskCache().put(originalKey, writer);

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, “Finished encoding source to cache”

  • ", key: " + originalKey

  • ", data: " + dataToCache

  • ", encoder: " + encoder

  • ", duration: " + LogTime.getElapsedMillis(startTime));

}

} finally {

loadData.fetcher.cleanup();

}

sourceCacheGenerator =

new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);

}

注释1:通过 cacheData()将数据缓存到 DataDiskCache中,dataToCache是在数据加载好,这个对象才有的,所以在一开始进来,这个方法是不会执行的。

注释2:通过 DataFetcher.loadData()请求数据。

具体的请求这里就不细讲了。只知道在请求成功,会调用 SourceGenerator.onDataReady()表示数据已经获取成功:

// SourceGenerator.java

public void onDataReady(Object data) {

DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();

if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {

dataToCache = data;

cb.reschedule(); // 1

} else {

cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,

loadData.fetcher.getDataSource(), originalKey);

}

}

注释1:如果数据可以被缓存,则调用 reschedule(),这个方法会在 SourceGenerator.startNext()中执行 cacheData()将数据和新的DataCacheKey一起保存在了磁盘中。最后返回true。

最终还是调用了 onDataFetcherReady()使得代码回到了 DecodeJob 中。

而DecodeJob就会对数据解码,和重新编码后缓存,调用到 EngineJob.onResourceReady()

最后 EnginJob会向主线程发送消息,最终告知 ViewTarget()执行成功并显示对应图片资源。

1.5 DecodeJob.run时序图


从上面的源码,我这边总结了一下 run的时序图,便于理解方法的调用:

在这里插入图片描述

到这里 load方法算是分析的七七八八了。

上述分析中缓存讲的并不是很清楚,所以这里需要再补充一些缓存的知识。

2. Glide缓存总结

==============================================================================

我们知道 Glide的缓存机制,都在 load()流程中。

一路看下来,我们知道了它总共有两种缓存,一种是 内存缓存,一种是 磁盘缓存:

2.1 内存缓存


内存缓存出现在 load的前半段:

// Engine.java

EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);

EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);

他们的作用:

  • 第一层内存缓存: loadFromActiveResources

ActiveResources所维护的 HashMap中获取弱引用缓存资源

  • 第二层内存缓存:loadFromCache

LruCache中获取缓存资源,LruCache就是一个拓展的 LinkedHashMap

如果可以获取成功, 把获取到的数据放到 ActiveResources 中。

这里的问题是,同样是内存缓存,为什么要分成两层?为什么第二层在获取成功后把数据移动到第一层?

我们需要注意的是在我们获取成功的时候,代码调用了: active.acquire() 资源有一个计数器,当被引用时,自增1。

这个地方就解释了为什么内存缓存有两层:

将内存缓存的资源分成 正在使用的 和 没有使用的,正在使用的用弱引用缓存,这样能优化运行时内存性能,在需要时随时回收,当前不用的图片则通过LruCache来缓存。这样也能节省空间

这里就解决了两层内存缓存的疑惑。

这里还有另外一个问题,那就是 资源是什么时候被放到 内存Cache里面去的呢?

在 1.5节中,我们看到了回调,在 Engine.onEngineJobComplete()中:

// Engine.java

public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {

Util.assertMainThread();

if (resource != null) {

resource.setResourceListener(key, this);

if (resource.isCacheable()) {

activeResources.activate(key, resource); // 1

}

}

jobs.removeIfCurrent(key, engineJob);

}

注释1:这个方法就是将资源放到了 ActiveResources中。

那只有这个地方,LruCache中的缓存在哪里存的呢?这就要看Resource资源了,我们在EngineResource中,看到它除了acquire()进行引用自增,它同样还有一个方法,如果减少一个引用,他就会自减:

// EngineResource.java

void release() {

if (–acquired == 0) {

listener.onResourceReleased(key, this);

}

}

最后

以前一直是自己在网上东平西凑的找,找到的东西也是零零散散,很多时候都是看着看着就没了,时间浪费了,问题却还没得到解决,很让人抓狂。

后面我就自己整理了一套资料,还别说,真香!

资料有条理,有系统,还很全面,我不方便直接放出来,大家可以先看看有没有用得到的地方吧。

系列教程图片

2020Android复习资料汇总.png

flutter

NDK

设计思想开源框架

微信小程序
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
什么内存缓存有两层:

将内存缓存的资源分成 正在使用的 和 没有使用的,正在使用的用弱引用缓存,这样能优化运行时内存性能,在需要时随时回收,当前不用的图片则通过LruCache来缓存。这样也能节省空间

这里就解决了两层内存缓存的疑惑。

这里还有另外一个问题,那就是 资源是什么时候被放到 内存Cache里面去的呢?

在 1.5节中,我们看到了回调,在 Engine.onEngineJobComplete()中:

// Engine.java

public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {

Util.assertMainThread();

if (resource != null) {

resource.setResourceListener(key, this);

if (resource.isCacheable()) {

activeResources.activate(key, resource); // 1

}

}

jobs.removeIfCurrent(key, engineJob);

}

注释1:这个方法就是将资源放到了 ActiveResources中。

那只有这个地方,LruCache中的缓存在哪里存的呢?这就要看Resource资源了,我们在EngineResource中,看到它除了acquire()进行引用自增,它同样还有一个方法,如果减少一个引用,他就会自减:

// EngineResource.java

void release() {

if (–acquired == 0) {

listener.onResourceReleased(key, this);

}

}

最后

以前一直是自己在网上东平西凑的找,找到的东西也是零零散散,很多时候都是看着看着就没了,时间浪费了,问题却还没得到解决,很让人抓狂。

后面我就自己整理了一套资料,还别说,真香!

资料有条理,有系统,还很全面,我不方便直接放出来,大家可以先看看有没有用得到的地方吧。

[外链图片转存中…(img-Sn07jTjh-1714851113353)]

[外链图片转存中…(img-bkUfhNi3-1714851113354)]

[外链图片转存中…(img-Dur0dU1Y-1714851113355)]

[外链图片转存中…(img-JyUV0X0n-1714851113356)]

[外链图片转存中…(img-u8R7z2Y5-1714851113356)]

[外链图片转存中…(img-pE0kS8Hb-1714851113357)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值