一.缓存Key
由上一章节源码分析可知,Glide框架的缓存Key在Engine类中生成。
1.截图Engine类在Glide源码位置
2.Engine类生成缓存Key
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
}
也就是使用EngineKeyFactory类创建EngineKey对象。
class EngineKeyFactory {
@SuppressWarnings("rawtypes")
public EngineKey buildKey(String id, Key signature, int width, int height,
ResourceDecoder cacheDecoder,
ResourceDecoder sourceDecoder, Transformation transformation, ResourceEncoder encoder,
ResourceTranscoder transcoder, Encoder sourceEncoder) {
return new EngineKey(id, signature, width, height, cacheDecoder, sourceDecoder, transformation, encoder,
transcoder, sourceEncoder);
}
}
其中,可以看出生成EngineKey对象时,传的参数比较多。比如图片的宽度width和图片的高度height都可以影响EngineKey对象的生成。也就是说我们使用Glide时,即使图片路径一样。但是如果操作了图片的宽高。那么就会生成不同的缓存EngineKey对象。
二.内存缓存
1.简介
默认情况下,Glide框架会自动开启内存缓存。也就是说,当我们使用Glide加载了一张图片之后,这张图片就会被缓存到内存当中。只要它没从内存中清除,那么下次使用Glide再加载这张图片都会直接从内存当中读取,而不用重新从网络或硬盘上读取了,这样无疑就可以大幅度提升图片的加载效率。
2.代码开启关闭
<1> 关闭
Glide.with(this).load(url).skipMemoryCache(true).into(imageView);
<2> 开启 默认就是开启,也就是说只要不操作skipMemoryCache。就是默认开启内存缓存的
Glide.with(this).load(url).skipMemoryCache(false).into(imageView);
3.源码
DrawableRequestBuilder类的skipMemoryCache方法
@Override
public DrawableRequestBuilder<ModelType> skipMemoryCache(boolean skip) {
super.skipMemoryCache(skip);
return this;
}
调用GenericRequestBuilder类的skipMemoryCache方法
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> skipMemoryCache(boolean skip) {
this.isCacheable = !skip;
return this;
}
也就是赋值GenericRequestBuilder类的isCacheable变量 此变量默认true也就是开启内存缓存。
private boolean isCacheable = true;
此变量通过本类的obtainRequest方法获取Request对象。
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
...
isCacheable,
...
);
}
因为此方法返回GenericRequest对象,所以我们下面看一下这个类
GenericRequest类的onSizeReady()方法
@Override
public void onSizeReady(int width, int height) {
...
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
...
}
然后调用Engine类的load方法。
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
final String id = fetcher.getId();
//生成缓存EnginKey 也就是Glide的缓存Key
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),transcoder, loadProvider.getSourceEncoder());
//如果内存中缓存到的值 不为空 则使用缓存中的数据回调 并且return
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
return new LoadStatus(cb, engineJob);
}
获取缓存方法loadFromCache方法源码
传参Key:Glide的缓存Key
传参isMemoryCacheable:是否缓存
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}
<1> 传参 不使用内存缓存 return null。
<2> 否则继续。
获取EngineResource对象
EngineResource<?> cached = getEngineResourceFromCache(key);
然后,将刚刚得到的 EngineResource对象+缓存Key+getReferenceQueue()方法获取的对象 生成一个软引用WeakReference<EngineResource<?>>对象。最后存储在Map中
//声明
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
//初始化
Engine类构造方法中
this.activeResources = activeResources;
软引用类
private static class ResourceWeakReference extends WeakReference<EngineResource<?>> {
private final Key key;
public ResourceWeakReference(Key key, EngineResource<?> r, ReferenceQueue<? super EngineResource<?>> q) {
super(r, q);
this.key = key;
}
}
获取软引用队列
private ReferenceQueue<EngineResource<?>> getReferenceQueue() {
if (resourceReferenceQueue == null) {
resourceReferenceQueue = new ReferenceQueue<EngineResource<?>>();
//获取MessageQueue
MessageQueue queue = Looper.myQueue();
//添加IdleHandler
queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue));
}
return resourceReferenceQueue;
}
小结1
<1> Glide的内存缓存开始于Engine类load方法。
<2> 存内存缓存时会通过key获取EngineResource对象。获取到缓存图片之后会先将它从缓存中移除,然后再重新将这个缓存图片存储到activeResources当中。activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片。
<3> Glide内存缓存底层用的是LruCache类
LruCache+DiskLruCache详解
https://blog.youkuaiyun.com/weixin_37730482/article/details/71191150
三.磁盘缓存
1.简介
默认情况下,Glide框架会自动开启磁盘缓存。
2.代码开启关闭
关闭
Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);
即调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能。
<1> DiskCacheStrategy.NONE: 表示不缓存任何内容。
<2> DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
<3> DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
<4> DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
3.源码
DrawableRequestBuilder类的diskCacheStrategy()方法
@Override
public DrawableRequestBuilder<ModelType> diskCacheStrategy(DiskCacheStrategy strategy) {
super.diskCacheStrategy(strategy);
return this;
}
调用
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> diskCacheStrategy(DiskCacheStrategy strategy) {
this.diskCacheStrategy = strategy;
return this;
}
流程图
由流程图到 DecodeJob类的loadFromCache方法
private Resource<T> loadFromCache(Key key) throws IOException {
File cacheFile = diskCacheProvider.getDiskCache().get(key);
if (cacheFile == null) {
return null;
}
Resource<T> result = null;
try {
result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
} finally {
if (result == null) {
diskCacheProvider.getDiskCache().delete(key);
}
}
return result;
}
//磁盘缓存接口 Glide框架中唯一实现类是Engine类的LazyDiskCacheProvider内部静态类
private final DiskCacheProvider diskCacheProvider;
//设置的磁盘缓存策略
private final DiskCacheStrategy diskCacheStrategy;
小结2
<1> Glide的磁盘缓存底层用的是DiskLruCache类。
LruCache+DiskLruCache详解
https://blog.youkuaiyun.com/weixin_37730482/article/details/71191150
四.自定义磁盘缓存
Key实现类
public class OriginalKey implements Key {
private final String id;
private final Key signature;
public OriginalKey(String id, Key signature) {
this.id = id;
this.signature = signature;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OriginalKey that = (OriginalKey) o;
if (!id.equals(that.id)) {
return false;
}
if (!signature.equals(that.signature)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + signature.hashCode();
return result;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
try {
messageDigest.update(id.getBytes(STRING_CHARSET_NAME));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
signature.updateDiskCacheKey(messageDigest);
}
}
Key管理类
public class SafeKeyGenerator {
private final LruCache<Key, String> loadIdToSafeHash = new LruCache<Key, String>(1000);
public String getSafeKey(Key key) {
String safeKey;
synchronized (loadIdToSafeHash) {
safeKey = loadIdToSafeHash.get(key);
}
if (safeKey == null) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
key.updateDiskCacheKey(messageDigest);
safeKey = Util.sha256BytesToHex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
synchronized (loadIdToSafeHash) {
loadIdToSafeHash.put(key, safeKey);
}
}
return safeKey;
}
}
外部调用类
public class GlideImageUtils {
private Context context;
public GlideImageUtils(Context context) {
this.context = context;
}
public File getCacheFile(String id) {
OriginalKey originalKey = new OriginalKey(id, EmptySignature.obtain());
SafeKeyGenerator safeKeyGenerator = new SafeKeyGenerator();
String safeKey = safeKeyGenerator.getSafeKey(originalKey);
try {
DiskLruCache diskLruCache = DiskLruCache.open(new File(context.getCacheDir(), DiskCache.Factory.DEFAULT_DISK_CACHE_DIR), 1, 1, DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE);
DiskLruCache.Value value = diskLruCache.get(safeKey);
if (value != null) {
return value.getFile(0);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
结果
首次进入页面
D/GlideActivity: file----:null
再次进入页面
D/GlideActivity: file----:/data/user/0/com.wjn.rxdemo/cache/image_manager_disk_cache/2355ef2bc78d689ec683e8db475ce6f36a817b5ad385235ecd94de1a1a3f8d3b.0
如果图片的路径后面有参数,说明图片的路径是变化的,但是图片不变。比如
http://url.com/image.jpg?token=d9caa6e02c990b0a
token可能会变,但是图片不变。这样的话要是不处理,就会每一个token对应一个图片。从而一张图片缓存多张图片。
由上代码可知
final String id = fetcher.getId();
id其实就是图片的url地址。那么,这里是通过调用fetcher.getId()方法来获取的图片url地址,而我们在上一篇文章中已经知道了,fetcher就是HttpUrlFetcher的实例。
getId()方法的源码
@Override
public String getId() {
return glideUrl.getCacheKey();
}
由上代码可知,getId()方法中又调用了GlideUrl的getCacheKey()方法。GlideUrl对象其实就是我们在load()方法中传入的图片url地址,然后Glide在内部把这个url地址包装成了一个GlideUrl对象。
GlideUrl的getCacheKey()方法的源码
public class GlideUrl {
private final URL url;
private final String stringUrl;
...
public GlideUrl(URL url) {
this(url, Headers.DEFAULT);
}
public GlideUrl(String url) {
this(url, Headers.DEFAULT);
}
public GlideUrl(URL url, Headers headers) {
...
this.url = url;
stringUrl = null;
}
public GlideUrl(String url, Headers headers) {
...
this.stringUrl = url;
this.url = null;
}
public String getCacheKey() {
return stringUrl != null ? stringUrl : url.toString();
}
...
}
创建一个MyGlideUrl继承自GlideUrl
public class MyGlideUrl extends GlideUrl {
private String mUrl;
public MyGlideUrl(String url) {
super(url);
mUrl = url;
}
@Override
public String getCacheKey() {
return mUrl.replace(findTokenParam(), "");
}
private String findTokenParam() {
String tokenParam = "";
int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
if (tokenKeyIndex != -1) {
int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
if (nextAndIndex != -1) {
tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
} else {
tokenParam = mUrl.substring(tokenKeyIndex);
}
}
return tokenParam;
}
}
使用
Glide.with(this)
.load(new MyGlideUrl(url))
.into(imageView);
五.Glide框架Application配置
项目Application级别管理Glide缓存
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
}
/**
* onLowMemory:系统整体内存较低时调用
*/
@Override
public void onLowMemory() {
super.onLowMemory();
Glide.get(getApplicationContext()).clearMemory();
Log.d("MyApplication", "onLowMemory方法执行");
}
/**
* 通知应用程序内存使用情况
* 每次退出APP时调用
*/
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == Glide.TRIM_MEMORY_UI_HIDDEN) {
Glide.get(getApplicationContext()).clearMemory();
}
Glide.get(getApplicationContext()).trimMemory(level);
Log.d("MyApplication", "onTrimMemory方法执行level----:" + level);
}
}
每次退出应用结果
D/MyApplication: onTrimMemory方法执行level----:20