上面的四个步骤就代表了Glide的缓存机制,当四步缓存中未查找对应的资源时再执行网络请求加载资源,加载完成后也会按照这四步缓存资源;
GlideApp.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
Glide提供以下
GlideApp.with(fragment)
.load(url)
.onlyRetrieveFromCache(true)
.into(imageView);
当开启仅从缓存加载去加载数据,除非加载的图片已经在内存缓存或磁盘缓存中,否则加载失败;
GlideApp.with(fragment)
.load(url)
.skipMemoryCache(true)
.into(view);
.diskCacheStrategy(DiskCacheStrategy.NONE)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
Glide 允许应用通过 AppGlideModule 实现来配置 Glide 的内存和磁盘缓存使用,可以配置缓存文件的位置和缓存控件的大小
builder.setDiskCache(new ExternalDiskCacheFactory(context));
builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context));
Glide内置3中设置存储位置的Factory
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));
@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));
@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));
Glide的内部缓存会根据多种条件创建最终缓存的Key,显然我们如果要刷新请求而不实用缓存就必须改变获取的键,使获取的和储存的键不一致,这样缓存失效就会加载新的数据,通常改变标识符比较困难或者根本不可能,所以Glide也提供了 签名 API 来混合额外数据到你的缓存键中,针对以下3种类型资源提供以下方案:
例子:第一次加载,将缓存的键混入数字“123”:
GlideApp.with(this)
.load("http://img.zcool.cn/community/[email protected]")
.placeholder(R.mipmap.ic_launcher)
.error(R.drawable.ic_launcher_background)
.signature(new ObjectKey("123"))
.transition(new DrawableTransitionOptions().crossFade())
.into(imageView);
.signature(new ObjectKey("456"))
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());
}}
new AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
Glide.get(applicationContext).clearDiskCache();
return null;
}}
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很多的功能都是在这初始化的,依次介绍上面实例化的对象:
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; // 设置内存缓存和Bitmap缓存池的大小
bitmapPoolSize = targetBitmapPoolSize;
} else {
float part = availableSize / (builder.bitmapPoolScreens + builder.memoryCacheScreens);
memoryCacheSize = Math.round(part * builder.memoryCacheScreens);
bitmapPoolSize = Math.round(part * builder.bitmapPoolScreens);
}
}
MemorySizeCalculator根据设备状态和页面的像素大小计算缓存大小,从上面计算可以知道计算结果如下:
由上一篇文章(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中,总结一下上面查找缓存的过程:
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);
}
}
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添加
}
}
}
}
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;
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从内存缓存资源中加载数据,具体执行流程:
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查找资源
由上面知道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);
}
@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey); //从活跃资源中移除
if (resource.isMemoryCacheable()) {
cache.put(cacheKey, resource); //添加到Cache缓存中
} else {
resourceRecycler.recycle(resource);
}
}
从上面分析知道,创建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;
}
上面的步骤就是整个获取缓存资源的过程,具体流程如下:
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获取缓存的原始资源
@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;
}
}
@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);
}
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
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中执行一下逻辑:
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中执行逻辑如下:
到此Glide的缓存机制就介绍完了,正式因为强大的缓存功能存在才使的我们开发中加载图片变得如此简单,通过对源码的分析能更好的使用框架和学习框架;