概述
1、Glide的缓存
- Glide 在加载图片时会依次访问以下缓存:
- 活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片?
- 内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中?
- 资源类型(Resource) - 该图片是否之前曾被解码、转换并写入过磁盘缓存?
- 数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?
上面的四个步骤就代表了Glide的缓存机制,当四步缓存中未查找对应的资源时再执行网络请求加载资源,加载完成后也会按照这四步缓存资源;
2、缓存策略
2.1、磁盘缓存
- DiskCacheStrategy: 设置Glide的缓存策略,通过设置缓存策略可选择性地控制缓存原数据还是缓存转换后的数据,或是二者都缓存
- 默认的策略叫做 AUTOMATIC,它会尝试对本地和远程图片使用最佳的策略
- AUTOMATIC : 当加载远程数据(比如,从URL下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过(比如,变换,裁剪)的原始数据;
- AUTOMATIC : 对于加载本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图,因为图片在本地比较容易直接获取;
- 修改指定的DiskCacheStrategy的缓存策略
GlideApp.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
Glide提供以下
- ALL : 缓存数据和资源的远程数据,以及仅用资源的本地数据。
- AUTOMATIC:默认的缓存策略
- DATA:在解码之前将检索到的数据直接写入磁盘缓存。
- NONE:不缓存
- RESOURCE:在解码后将资源写入磁盘。
2.2、 仅从缓存加载
GlideApp.with(fragment)
.load(url)
.onlyRetrieveFromCache(true)
.into(imageView);
当开启仅从缓存加载去加载数据,除非加载的图片已经在内存缓存或磁盘缓存中,否则加载失败;
2.3、跳过缓存
- skipMemoryCache():仅跳过内存缓存
GlideApp.with(fragment)
.load(url)
.skipMemoryCache(true)
.into(view);
- DiskCacheStrategy.NONE: 跳过磁盘缓存
.diskCacheStrategy(DiskCacheStrategy.NONE)
- 两者都跳过
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
3、配置缓存
Glide 允许应用通过 AppGlideModule 实现来配置 Glide 的内存和磁盘缓存使用,可以配置缓存文件的位置和缓存控件的大小
- 配置缓存位置
(3)磁盘缓存
Glide 使用 DiskLruCacheWrapper 作为默认的 磁盘缓存 , DiskLruCacheWrapper 是一个使用 LRU 算法的固定大小的磁盘缓存,默认磁盘大小为 250 MB ,位置在手机内存中应用的缓存文件夹 中的一个 特定目录
- 设置缓存存储位置
builder.setDiskCache(new ExternalDiskCacheFactory(context));
builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context));
Glide内置3中设置存储位置的Factory
- InternalCacheDiskCacheFactory:缓存在手机内存的缓存文件中
- ExternalDiskCacheFactory:设置缓存文件在外部存储中
- ExternalPreferredCacheDiskCacheFactory:如果之前存储在内部存储中,则会维持不变继续使用,否则使用外部缓存文件;
- 应用程序都可以改变磁盘缓存的大小
int diskCacheSizeBytes = 1024 1024 100; 100 MB
builder.setDiskCache(new InternalDiskCacheFactory(context, diskCacheSizeBytes));
- 应用程序还可以改变缓存文件夹在外存或内存上的名字:
int diskCacheSizeBytes = 1024 1024 100; 100 MB
builder.setDiskCache( new InternalDiskCacheFactory(context, cacheFolderName, diskCacheSizeBytes));
- 自定义 MemoryCache的大小,具体是在它们的 AppGlideModule 中使用applyOptions(Context, GlideBuilder) 方法配置 MemorySizeCalculator
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
super.applyOptions(context, builder);
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context) //Glide内部默认配置
.setMemoryCacheScreens(2)
.build();
builder.setMemoryCache(new LruResourceCache(calculator.getBitmapPoolSize()));
}
- 直接限定缓存的大小
int memoryCacheSizeBytes = 1024 * 1024 * 20;
builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
3.1、Bitmap池
- Glide 使用 LruBitmapPool 作为默认的 BitmapPool
- 在 AppGlideModule 中定制 BitmapPool的尺寸,使用 applyOptions(Context, GlideBuilder) 方法并配置 MemorySizeCalculator:
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
super.applyOptions(context, builder);
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
.setMemoryCacheScreens(2)
.build();
builder.setBitmapPool(new LruBitmapPool(calculator.getBitmapPoolSize()));
}
- 直接限定大小
int bitmapPoolSizeBytes = 1024 * 1024 * 30; // 30mb
builder.setBitmapPool(new LruBitmapPool(bitmapPoolSizeBytes));
4、缓存的刷新
4.1、定制缓存刷新策略
Glide的内部缓存会根据多种条件创建最终缓存的Key,显然我们如果要刷新请求而不实用缓存就必须改变获取的键,使获取的和储存的键不一致,这样缓存失效就会加载新的数据,通常改变标识符比较困难或者根本不可能,所以Glide也提供了 签名 API 来混合额外数据到你的缓存键中,针对以下3种类型资源提供以下方案:
- MediaStore 内容 - 对于媒体存储内容,可以使用Glide的 MediaStoreSignature 类作为签名,MediaStoreSignature允许你混入修改时间、MIME类型,以及item的方向到缓存键中,这三个属性能够可靠地捕获对图片的编辑和更新,这可以允许缓存媒体存储的缩略图。
- 文件 - 使用 ObjectKey 来混入文件的修改日期,改变日期时即可刷新缓存
- Url - 针对Url有两种有限方法:1、让 server 保证在内容变更时对URL做出改变;2使用 ObjectKey 来混入任意数据来改变Url
例子:第一次加载,将缓存的键混入数字“123”:
GlideApp.with(this)
.load("http://img.zcool.cn/community/01638059302785a8012193a36096b8.jpg@2o.jpg")
.placeholder(R.mipmap.ic_launcher)
.error(R.drawable.ic_launcher_background)
.signature(new ObjectKey("123"))
.transition(new DrawableTransitionOptions().crossFade())
.into(imageView);
- 将手机断开网络重新进入程序,因为有缓存的存在所以可以直接加载图片,现在修改程序中键掺入的123为456:
.signature(new ObjectKey("456"))
- 此时网络为断开,且再次请求的键与之前缓存的并不相同,此时加载失败,连接网络后会重新拉取图片并缓存,图片加载成功
- 此时内存中是有两个缓存图片,分别对应两次请求所生成的键
- 也定义你自己的签名,只要实现 Key 接口就好,并重写其中的方法,还记得之前看过处理七牛云图片Url的文章就是自定义Key保证缓存有效
public class IntegerVersionSignature implements Key {
private int currentVersion;
public IntegerVersionSignature(int currentVersion) {
this.currentVersion = currentVersion;
}
@Override
public boolean equals(Object o) {
if (o instanceof IntegerVersionSignature) {
IntegerVersionSignature other = (IntegerVersionSignature) o;
return currentVersion = other.currentVersion;
}
return false;
}
@Override
public int hashCode() {
return currentVersion;
}
@Override
public void updateDiskCacheKey(MessageDigest md) {
messageDigest.update(ByteBuffer.allocate(Integer.SIZE).putInt(signature).array());
}}
5、清除缓存
- 清理缓存:使用 clearDiskCache清理所有磁盘缓存条目
new AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
Glide.get(applicationContext).clearDiskCache();
return null;
}}
6、缓存机制源码分析
6.1、Glide中缓存的配置
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor(); //实例化执行网络资源加载线程池
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); //实例化执行磁盘缓存的线程池
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); //实例化计算内存缓存大小
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size); //初始化Bitmap池
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context); //实例化磁盘缓存工厂
}
if (engine == null) { //将设置的信息初始化Engine
engine = new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList(); //请求监听器
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
);
}
Glide很多的功能都是在这初始化的,依次介绍上面实例化的对象:
- sourceExecutor:执行网络资源加载的线程池——只有最多4个核心线程的线程池
- diskCacheExecutor:执行磁盘缓存线程池——只有一个核心线程的线程池
- bitmapPool:初始化BitmapPool缓存池
- diskCacheFactory:实例化磁盘工厂——实例化Glide存储位置
- memorySizeCalculator:自动计算缓存空间大小
MemorySizeCalculator(MemorySizeCalculator.Builder builder) {
arrayPoolSize = isLowMemoryDevice(builder.activityManager) //(1)判断设备内存ArrayPool正常设备4M,低内存设备2M
? builder.arrayPoolSizeBytes / LOW_MEMORY_BYTE_ARRAY_POOL_DIVISOR
: builder.arrayPoolSizeBytes;
int maxSize = // 根据设备内存确定缓存大小,低内存设备*0.33f ,高内存设备 * 0.4f
getMaxSize( builder.activityManager, builder.maxSizeMultiplier, builder.lowMemoryMaxSizeMultiplier);
int widthPixels = builder.screenDimensions.getWidthPixels();
int heightPixels = builder.screenDimensions.getHeightPixels();
int screenSize = widthPixels * heightPixels * BYTES_PER_ARGB_8888_PIXEL;// 获取设备的宽高像素,计算一屏幕包含的像素大小
int targetBitmapPoolSize = Math.round(screenSize * builder.bitmapPoolScreens);
int targetMemoryCacheSize = Math.round(screenSize * builder.memoryCacheScreens); // 计算各自缓存所需要的内存空间
int availableSize = maxSize - arrayPoolSize; // 目前可分配的内存空间
if (targetMemoryCacheSize + targetBitmapPoolSize <= availableSize) {
memoryCacheSize = targetMemoryCacheSize; // 如果剩余内存够用,直接设置
bitmapPoolSize = targetBitmapPoolSize;
} else {
float part = availableSize / (builder.bitmapPoolScreens + builder.memoryCacheScreens); // 可用内存不足时,需要看比例均分
memoryCacheSize = Math.round(part * builder.memoryCacheScreens);
bitmapPoolSize = Math.round(part * builder.bitmapPoolScreens);
}
}
MemorySizeCalculator根据设备状态和页面的像素大小计算缓存大小,从上面计算可以知道计算结果如下:
- bitmapPool的缓存大小为一张全屏图片大小的4倍(8.0之后为1倍)
- LrArray的缓存大小为4M 或2M(低内存设备)
- Memory缓存大小为一张全屏图片大小的2倍(1080 * 1920 8M左右)
- 磁盘默认缓存为250M
6.2、缓存源码分析
由上一篇文章(Android进阶知识树——Glide源码分析)的分析知道,真正执行加载数据的是在Engine.load()方法中,现在重新看一下这个方法:
public synchronized <R> LoadStatus load() {
//创建缓存的Key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); //(1)从活跃的引用中加载缓存信息
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); //(2)从缓存中获取缓存实例
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
}
首先介绍下ActiveResource的作用,如果哟个图片当前正在被引用会保存在ActiveResource中,总结一下上面查找缓存的过程:
- 首先根据请求的地址和条件生成缓存Key,影响Key的因素很多
- 首先从ActiveResource中获取资源,获取到则返回资源,否则继续向下执行
- 从内存缓存中获取,获取到则返回资源
- 缓存Key的生成
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options); //传入的参数都影响Key的生成,默认EngineKeyFactory
class EngineKeyFactory {
EngineKey buildKey(Object model, Key signature, int width, int height,
Map<Class<?>, Transformation<?>> transformations, Class<?> resourceClass,Class<?> transcodeClass, Options options) {
return new EngineKey(model, signature, width, height, transformations, resourceClass,
transcodeClass, options);
}
}
6.3、从内存缓存中加载资源
- Glide的引用计数
Glide中采用计数的方式统计资源的引用,在每个EngineResource内部都设置一个引用计数acquired,在加载资源时引用++,释放资源时引用—:
class EngineResource<Z> implements Resource<Z> {
private int acquired; //统计引用数
synchronized void acquire() {
++acquired; //引用数 ++
}
void release() {
synchronized (this) {
if (--acquired == 0) { //每次释放时,引用数 --
listener.onResourceReleased(key, this); //上面Cache添加
}
}
}
}
- 引用数++:从活跃引用资源中、内存缓存中、资源加载中使用获得的资源时,资源的引用数 +1
- 引用数—:资源释放时引用数–
- 当引用数为0时,则将资源转移到Cache缓存中
- loadFromActiveResources(key, isMemoryCacheable)
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) { //(1)如果跳过内存缓存直接返回null
return null;
}
EngineResource<?> active = activeResources.get(key); //(2)从activeResources中获取缓存资源
if (active != null) {
active.acquire(); //(3)资源的引用 +1 (Glide采用技术引用)
}
return active;
}
loadFromActiveResources()从活跃的引用中加载缓存资源,Glide的内部采用计数的方式统计资源的引用,当资源被引用时计数+1,资源释放是计数-1;
- loadFromCache(key, isMemoryCacheable)
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {//(1)是否跳过缓存
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire(); //(3)对资源的引用 +1
activeResources.activate(key, cached); //(4)将引用的资源添加到activeResources中
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key); // (2)从内存缓存中获取数据,并从cache中移除
return result;
}
loadFromCache从内存缓存资源中加载数据,具体执行流程:
- 首先判断Glide加载是否跳过缓存,如果跳过则直接返回null
- 从cache缓存中获取资源,对资源的应用数+1,并将资源放如activeResources缓存中
- LruResourceCache
private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true); //内存缓存Map
@Nullable
public synchronized Y remove(@NonNull T key) {
final Y value = cache.remove(key); //从Map中获取数据
if (value != null) {
currentSize -= getSize(value); //缓存数减1
}
return value;
}
上面流程中的cache实际是LruResourceCache的实例,在初始化Glide过程中传入,LruResourceCache继承了LruCache也实现了MemoryCache(适配器模式),在LruCache中使用可LinknHashMap缓存数据,在获取时从cache中根据key查找资源
6.4、 内存缓存写入
- ActiveResources的写入
由上面知道ActiveResources中保存当前正在引用的资源,ActiveResources中的资源的写入主要从Cache中获取资源后添加的
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
public synchronized void onEngineJobComplete( EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null && resource.isMemoryCacheable()) {
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
- Cache的缓存写入
在内存中缓存使用过但此时未使用的资源缓存到Cache中,这句话简单的理解就是当活跃资源没有引用时即资源引用数为0时,添加到Cache缓存中
@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey); //从活跃资源中移除
if (resource.isMemoryCacheable()) {
cache.put(cacheKey, resource); //添加到Cache缓存中
} else {
resourceRecycler.recycle(resource);
}
}
6.5、磁盘缓存读取
- EngineJob、DecodeJob:开启任务去加载(磁盘或网络)
从上面分析知道,创建EngineJob、DecodeJob实例后,调用EngineJob.start执行资源加载,在上一篇文章中我们直接分析了网络请求,本篇看一下如何从缓存文件中获取资源的:
public synchronized void start(DecodeJob<R> decodeJob) {
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
boolean willDecodeFromCache() {
Stage firstStage = getNextStage(Stage.INITIALIZE); //获取下一步执行的状态,
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE; //返回次两种状态表示存在原文件缓存或存在转换后的文件缓存
}
private Stage getNextStage(Stage current) {
}
关于getNextStage()具体流程见上一篇文章,这里首先根据有无缓存情况,获取对应执行的线程池,上面设置Stage.RESOURCE_CACHE后,如果有转换后的缓存文件存在,则会执行的就是ResourceCacheGenerator.start()
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys(); //
while (modelLoaders == null || !hasNextModelLoader()) {
Key sourceId = sourceIds.get(sourceIdIndex); //获取缓存的KeyId
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
currentKey = new ResourceCacheKey( //将数据封装到ResourceCacheKey中
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
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 getDiskCache() {
return diskCacheProvider.getDiskCache();
}
//LazyDiskCacheProvider:
@Override
public DiskCache getDiskCache() {
if (diskCache == null) {
synchronized (this) {
if (diskCache == null) {
//调用InternalCacheDiskCacheFactory 配置缓存目录,并创建DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
diskCache = factory.build();
}
}
}
return diskCache;
}
@Override
public File get(Key key) { //获取缓存资源File
File result = null;
try {
final DiskLruCache.Value value = getDiskCache().get(safeKey); //从DiskLruCache磁盘缓存中获取缓存文件
if (value != null) {
result = value.getFile(0);
}
}
return result;
}
上面的步骤就是整个获取缓存资源的过程,具体流程如下:
- 调用DecodeHelper方法获取缓存文件的Key列表获取缓存资源的Key,创建ResourceCacheKey实例封装Key和请求信息
- 调用getDiskCache()通过diskCacheProvider最终调用设置的DiskCacheFactory配置缓存目录,并创建DiskLruCacheWrapper.create(cacheDir, diskCacheSize)
- 根据ResourceCacheKey加载缓存数据,调用DiskLruCacheWrapper.get()从磁盘缓存中获取缓存文件
- 调用 loadData.fetcher.loadData(helper.getPriority(), this);从缓存的文件中读取资源
- loadData.fetcher.loadData(helper.getPriority(), this):从缓存文件中读取资源,要分析这里的loadData就要从开始说起,创建Glide时根据获取资源的方式不同注册了许多ModelLoadfactory分别处理不同的数据类型
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())
此处从File中获取所以获取的是FileLoader.StreamFactory(),最终创建FileLoader实例及modelLoader,调用buildLoadData创建loadData,传入FileFetcher实例,所以最终加载数据的是FileFetcher.loadData(),loadData()中直接使用FileOpener读取文件资源并回调onDataReady()
@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));
}
//FileFetcher
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
try {
data = opener.open(file); //执行FileOpener中方法直接从文件File中获取数据:return new FileInputStream(file);
}
callback.onDataReady(data); //回调ResourceCacheGenerator.此时从文件中获取资源
}
上面的流程是获取转换后的资源过程,如果获取成功则进行执行成功后的回调并显示图片,若获取失败或资源不存在则继续执行,这里看一下上面的判断逻辑
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) { // 如果加载转换后的资源失败,此处会返回false
stage = getNextStage(stage); // 此时的State为RESOURCE_CACHE,再次获取会返回DATA_CACHE
currentGenerator = getNextGenerator(); //执行DataCacheGenerator()获取源缓存文件;
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
从上面的While循环知道,当处理后的缓存资源获取失败时则执行下一个Stage,此时State为RESOURCE_CACHE执行next后获取就是DATA_CACHE,则执行的是DataCacheGenerator加载原始缓存数据,整个获取流程和上面一致,只是此时传递的是DataCacheKey即originalKey即根据originalKey获取缓存的原始资源
6.6、磁盘缓存的写入
- SourceGenerator——执行网络请求加载完成后回调onDataReady()
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
//(1)判断是否允许磁盘缓存原始资源
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
cb.reschedule(); //(2)执行DecodeJob.reschedule()
}
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; //设置runReason标识
callback.reschedule(this); //方法回调EngineJob,然后线程池执行最终会切换磁盘缓存线程,执行Decode.run()
}
由于runReason = RunReason.SWITCH_TO_SOURCE_SERVICE,方法直接执行到runGenerators()中,
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
}
}
- 详细过程见注释,之前分析过此方法,首先判断Glide是否允许磁盘缓存,如果允许则执行cb.reschedule(),最终还是会再次执行SourceGenerator.startNext(),第二次回调SourceGenerator.startNext()时执行cacheData()缓存文件,注意此时的dataToCache被第一次执行时赋值了,cacheData()中调用 helper.getDiskCache().put(originalKey, writer)写入文件
@Override
public boolean startNext() { //第二次调用startNext
if (dataToCache != null) {
cacheData(data); // 缓存源文件
}
return started;
}
private void cacheData(Object dataToCache) {
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);
}
}
- 继续执行startNext(),在执行cacheData()缓存Data之后会调用sourceCacheGenerator.startNext()程序进入DataCacheGenerator.startNext()
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
- DataCacheGenerator.startNext():
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
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); //(2)从文件File中加载资源,见上面的磁盘缓存的读取
}
}
return started;
}
startNext中执行一下逻辑:
- 首先根据Key从缓存文件中获取缓存的原始文件资源
- 调用loadData.fetcher.loadData()从缓存文件中读取资源
- 资源读取后回调DecodeJob.onDataFetcherReady(),onDataFetcherReady()中调用decodeFromRetrievedData()完成缓存资源的解析和转换
private void decodeFromRetrievedData() {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);//转换文件
notifyEncodeAndRelease(resource, currentDataSource); // 通知加载成功,返回转换后的资源(见源码分析)
}
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);
}
}
}
void encode(DiskCacheProvider diskCacheProvider, Options options) {
try {
diskCacheProvider.getDiskCache().put(key, //调用DiskLruCacheWrapper缓存转换后的资源
new DataCacheWriter<>(encoder, toEncode, options));
} finally {
}
}
DecodeJob中执行逻辑如下:
- 执行decodeFromData()转换文件
- 调用notifyEncodeAndRelease()返回转换后的资源
- 执行 deferredEncodeManager.encode(diskCacheProvider, options)解析转换资源
- 执行成功后回调资源完成图片的显示
到此Glide的缓存机制就介绍完了,正式因为强大的缓存功能存在才使的我们开发中加载图片变得如此简单,通过对源码的分析能更好的使用框架和学习框架;
最后
以上就是时尚母鸡为你收集整理的Android框架源码分析——Glide缓存机制源码分析的全部内容,希望文章能够帮你解决Android框架源码分析——Glide缓存机制源码分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复