前言
Glide, 该功能非常强大 Android 图片加载开源框架 相信大家并不陌生
正由于他的功能强大, 所以它的源码非常复杂, 这导致很多人望而却步
本人尝试将 Glide 的功能进行分解, 并单独针对每个功能进行源码分析, 从而降低 Glide 源码的复杂度
接下来, 我将推出一系列关于 Glide 的功能源码分析, 有兴趣可以继续关注
今天, 我将主要讲解在使用 Glide 缓存功能时的问题: 为什么 Glide 的缓存无起作用, 希望你们会喜欢
请先阅读文章 Android 源码分析: 手把手带你分析 Glide 的缓存功能
1. 背景
Glide 实现内存 & 磁盘缓存是根据 图片的缓存 Key 进行唯一标识
开发者为了降低成本 & 安全, 往往会将图片存放在云服务器上
如 七牛云 等等
为了保护 客户的图片资源, 图片云服务器 会在图片 Url 地址的基础上再加一个 token 参数
http://url.com/image.jpg?token=a6cvva6b02c670b0a
Glide 加载该图片时, 会使用加了 token 参数的图片 Url 地址 作为
id 参数, 从而生成 缓存 Key
2. 问题
作为身份认证的 token 参数可能会发生变化, 并不是一成不变
若 token 参数变了, 则图片 Url 跟着变, 则生成缓存 key 的所需 id 参数发生变化, 即 缓存 Key 也会跟着变化
这导致同一张图片, 但因为 token 参数变化, 而导致缓存 Key 发生变化, 从而使得 Glide 的缓存功能失效
缓存 Key 发生变化, 即同一个图片的当前缓存 key 和 之前写入缓存的 key 不相同, 这意味着 在读取缓存时 无法根据当前缓存 key 找到之前的缓存, 从而使得失效
3. 解决方案
3.1 原理
在 生成缓存 Key 的 id 参数 前, 将 带有 token 参数的图片 Url 地址 去掉 token 参数, 从而根据 初始的图片 Url 地址 生成缓存 Key 的 id 参数
实现了一个图片的缓存 Key 的 id 参数始终唯一 , 即等于 图片 Url 地址
3.2 储备知识: 生成缓存 Key 的 id 参数的逻辑
生成缓存 Key 的 id 参数的逻辑为: 直接将图片的 URL 地址作为缓存 Key 的 id 参数
回看文章 Android 源码分析: 手把手带你分析 Glide 的缓存功能生成缓存 Key 的代码
- public class Engine implements EngineJobListener,
- MemoryCache.ResourceRemovedListener,
- EngineResource.ResourceListener {
- 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();
- long startTime = LogTime.getLogTime();
- final String id = fetcher.getId();
- // 获得了一个 id 字符串, 即需加载图片的唯一标识
- // 如, 若图片的来源是网络, 那么该 id = 这张图片的 url 地址
- // fetcher = HttpUrlFetcher 的实例, 即调用 HttpUrlFetcher.getid()->> 分析 19
- EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(), loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(), transcoder, loadProvider.getSourceEncoder());
- // 将该 id 和 signaturewidthheight 等 10 个参数一起传入到缓存 Key 的工厂方法里, 最终创建出一个 EngineKey 对象
- // 创建原理: 通过重写 equals() 和 hashCode(), 保证只有传入 EngineKey 的所有参数都相同情况下才认为是同一个 EngineKey 对象
- // 该 EngineKey 即 Glide 中的缓存 Key
- ...
- }
<-- 分析 19:getId() -->
- public class HttpUrlFetcher implements DataFetcher<InputStream> {
- ...
- private final GlideUrl glideUrl;
- // GlideUrl = 在上篇文章讲解 图片加载 第 2 步 load() 中传入图片 url 地址时, Glide 在内部把图片 url 地址包装成一个 GlideUrl 对象
- @Override
- public String getId() {
- return glideUrl.getCacheKey();
- // ->> 分析 20
- }
<-- 分析 20:getCacheKey() -->
- public class GlideUrl {
- private final URL url;
- private final String stringUrl;
- ...
- // GlideUrl 构造函数
- public GlideUrl(URL url) {
- this(url, Headers.DEFAULT);
- }
- public GlideUrl(String url) {
- this(url, Headers.DEFAULT);
- }
- public String getCacheKey() {
- return stringUrl != null ? stringUrl : url.toString();
- // 在生成 GlideUrl 对象时:
- // 若传入的是 URL 字符串 (即图片地址), 就直接返回该字符串 (大多数是这种情况)
- // 若传入的是 URL 对象, 那么就返回这个对象 toString() 后的结果
- }
- ...
- }
3.3 实现方案
即 我们只需重写 getCacheKey() & 将 带有 token 参数的图片 Url 地址 去掉 token 参数 即可
- /**
- * 代码实现: 创建一个 GlideUrl 类的子类 & 重写 getCacheKey()
- **/
- // 1. 继承 GlideUrl
- public class mGlideUrl extends GlideUrl {
- private String mUrl;
- // 构造函数里 传入 带有 token 参数的图片 Url 地址
- public MyGlideUrl(String url) {
- super(url);
- mUrl = url;
- }
- // 2. 重写 getCacheKey()
- @Override public String getCacheKey() {
- return mUrl.replace(deleteToken(), "");
- // 通过 deleteToken() 从 带有 token 参数的图片 Url 地址中 去掉 token 参数
- // 最终返回一个没有 token 参数初始的图片 URL 地址
- // ->> 分析 1
- }
- // 分析 1:deleteToken()
- private String deleteToken() {
- 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;
- }
- }
- /**
- * 使用缓存时: 需要在 load() 中传入自定义的 mGlideUrl 对象
- **/
- Glide.with(this).load(new mGlideUrl(url)).into(imageView);
- // 注: a. 若像之前直接传入图片的 url 地址, 那么在内部还是会使用原始的 GlideUrl 类
- // b. 即直接将传入传入图片的 url 地址作为缓存 key 的 Id 参数, 而没有对 token 参数作任何处理
4. 总结
本文主要对 Glide 的图片缓存功能的使用问题进行讲解
来源: http://blog.csdn.net/carson_ho/article/details/79256773