在线咨询
QQ咨询
服务热线
服务热线:13125520620
TOP

Android 图片加载的那些事:为什么你的Glide 缓存没有起作用?

发布时间:2018-2-6 浏览:3263

1. 背景
 
Glide实现内存 & 磁盘缓存是根据 图片的缓存Key进行唯一标识
开发者为了降低成本 & 安全,往往会将图片存放在云服务器上 
如 七牛云 等等。
为了保护 客户的图片资源,图片云服务器 会在图片Url地址的基础上再加一个token参数
http://url.com/image.jpg?token=a6cvva6b02c670b0a
1
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 和 signature、width、height等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参数作任何处理
软件定制,博远电子,友情提醒。

TAG
软件定制,软件开发,瀚森HANSEN,辽宁,沈阳,抚顺
0
该内容对我有帮助