glide溯源之Key和缓存文件对应

   glide溯源系列文章

       看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);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_23081779/article/details/82784155