看glide源码的时候,对glide关于磁盘缓存文件名、内存缓存文件和资源请求路径的对应关系很好奇,于是来了看看glide是怎么把这个问题处理好的兴趣。glide中定义了抽象接口Key来做各种唯一标识,所有Key的实现类下面有说明,实现方式类似,而Key和磁盘缓存文件名的关系通过SafeKeyGenerator中的getSafeKey方法来关联,本文分析分为三部分,相关类介绍、MessageDigest用法和源码分析。
一、相关类介绍
1. SafeKeyGenerator:唯一字符串标识生成类,通过key来生成,此类是建立文件名和key之间的关系
2. interface Key,实现类:
* ResourceCacheKey:资源缓存key
* DataCacheKey:原始资源数据缓存key
* EngineKey:引擎加载key,用于唯一标识一次加载
* GlideUrl:glide中唯一标识url的包装
* ObjectKey:通用类型key
* MediaStoreSignature:媒体存储签名
* EmptySignature:空key,用于判断其他key是否为空
* Options:内存缓存key的设置 的唯一标识
* UniqueKey:空的key
二、MessageDigest的用法(前置知识)
//字符串生成md5值
public static String stringMD5(String input) {
try {
// 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”)
MessageDigest messageDigest =MessageDigest.getInstance("MD5");
// 输入的字符串转换成字节数组
byte[] inputByteArray = input.getBytes();
// inputByteArray是输入字符串转换得到的字节数组
messageDigest.update(inputByteArray);
// 转换并返回结果,也是字节数组,包含16个元素
byte[] resultByteArray = messageDigest.digest();
// 字符数组转换成字符串返回
return byteArrayToHex(resultByteArray);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
三、通过Key生成string即磁盘缓存文件名
public class SafeKeyGenerator {
//存储Ket和string的对应关系,最多缓存1000个
private final LruCache<Key, String> loadIdToSafeHash = new LruCache<>(1000);
private final Pools.Pool<PoolableDigestContainer> digestPool = FactoryPools.threadSafe(10,
new FactoryPools.Factory<PoolableDigestContainer>() {
@Override
public PoolableDigestContainer create() {
try {
return new PoolableDigestContainer(MessageDigest.getInstance("SHA-256"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
});
//获取Key对应的string
public String getSafeKey(Key key) {
String safeKey;
synchronized (loadIdToSafeHash) {
safeKey = loadIdToSafeHash.get(key);
}
if (safeKey == null) {
safeKey = calculateHexStringDigest(key);
}
synchronized (loadIdToSafeHash) {
loadIdToSafeHash.put(key, safeKey);
}
return safeKey;
}
//根据Key计算对应的String
private String calculateHexStringDigest(Key key) {
//获取一个含有messageDigest的包装对象
PoolableDigestContainer container = Preconditions.checkNotNull(digestPool.acquire());
try {
//此方法本质是把messgaeDigest传到key中,使用messageDigest.update(key的几个参数对应的bytes)
key.updateDiskCacheKey(container.messageDigest);
// calling digest() will automatically reset()
//通过sha256算法将Key转化为字符串返回
return Util.sha256BytesToHex(container.messageDigest.digest());
} finally {
digestPool.release(container);
}
}
private static final class PoolableDigestContainer implements FactoryPools.Poolable {
@Synthetic final MessageDigest messageDigest;
private final StateVerifier stateVerifier = StateVerifier.newInstance();
PoolableDigestContainer(MessageDigest messageDigest) {
this.messageDigest = messageDigest;
}
@NonNull
@Override
public StateVerifier getVerifier() {
return stateVerifier;
}
}
}
上述代码中用到了以下方法,也同时贴出来
// ResourceCacheKey,key有很多实现类,但实现方式类似,此处用ResouceCacheKey来举例说明
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
byte[] dimensions = arrayPool.getExact(8, byte[].class);
ByteBuffer.wrap(dimensions).putInt(width).putInt(height).array();
signature.updateDiskCacheKey(messageDigest);
sourceKey.updateDiskCacheKey(messageDigest);
messageDigest.update(dimensions);
if (transformation != null) {
transformation.updateDiskCacheKey(messageDigest);
}
options.updateDiskCacheKey(messageDigest);
messageDigest.update(getResourceClassBytes());
arrayPool.put(dimensions);
}
// Util
public static String sha256BytesToHex(@NonNull byte[] bytes) {
synchronized (SHA_256_CHARS) {
return bytesToHex(bytes, SHA_256_CHARS);
}
}