Glide使用高级技巧(解决Glide生成缓存Key问题)

2019-05-09 11:46:30

参考地址 Glide使用高级技巧(解决Glide生成缓存Key问题)

虽说Glide将缓存功能高度封装之后,使得用法变得非常简单,但同时也带来了一些问题。

比如之前有一位群里的朋友就跟我说过,他们项目的图片资源都是存放在七牛云上面的,而七牛云为了对图片资源进行保护,会在图片url地址的基础之上再加上一个token参数。也就是说,一张图片的url地址可能会是如下格式:

String url = "https://unsplash.it/200/200?random&55&token=d9caa6e02c990b0a";1

而使用Glide加载这张图片的话,也就会使用这个url地址来组成缓存Key。

但是接下来问题就来了,token作为一个验证身份的参数并不是一成不变的,很有可能时时刻刻都在变化。而如果token变了,那么图片的url也就跟着变了,图片url变了,缓存Key也就跟着变了。结果就造成了,明明是同一张图片,就因为token不断在改变,导致Glide的缓存功能完全失效了。

这其实是个挺棘手的问题,而且我相信绝对不仅仅是七牛云这一个个例,大家在使用Glide的时候很有可能都会遇到这个问题。

那么该如何解决这个问题呢?我们还是从源码的层面进行分析,首先再来看一下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();
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());

        ...
    }

    ...
}1234567891011121314151617181920

来看一下第11行,刚才已经说过了,这个id其实就是图片的url地址。那么,这里是通过调用fetcher.getId()方法来获取的图片url地址,而我们在上一篇文章中已经知道了,fetcher就是HttpUrlFetcher的实例,我们就来看一下它的getId()方法的源码吧,如下所示:

public class HttpUrlFetcher implements DataFetcher<InputStream> {

    private final GlideUrl glideUrl;
    ...    public HttpUrlFetcher(GlideUrl glideUrl) {        this(glideUrl, DEFAULT_CONNECTION_FACTORY);
    }

    HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {        this.glideUrl = glideUrl;        this.connectionFactory = connectionFactory;
    }

    @Override    public String getId() {        return glideUrl.getCacheKey();
    }

    ...
}123456789101112131415161718192021

可以看到,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();
    }    ...}1234567891011121314151617181920212223242526272829303132

这里我将代码稍微进行了一点简化,这样看上去更加简单明了。GlideUrl类的构造函数接收两种类型的参数,一种是url字符串,一种是URL对象。然后getCacheKey()方法中的判断逻辑非常简单,如果传入的是url字符串,那么就直接返回这个字符串本身,如果传入的是URL对象,那么就返回这个对象toString()后的结果。

其实看到这里,我相信大家已经猜到解决方案了,因为getCacheKey()方法中的逻辑太直白了,直接就是将图片的url地址进行返回来作为缓存Key的。那么其实我们只需要重写这个getCacheKey()方法,加入一些自己的逻辑判断,就能轻松解决掉刚才的问题了。

创建一个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;
    }

}1234567891011121314151617181920212223242526272829

可以看到,这里我们重写了getCacheKey()方法,在里面加入了一段逻辑用于将图片url地址中token参数的这一部分移除掉。这样getCacheKey()方法得到的就是一个没有token参数的url地址,从而不管token怎么变化,最终Glide的缓存Key都是固定不变的了。

当然,定义好了MyGlideUrl,我们还得使用它才行,将加载图片的代码改成如下方式即可:

Glide.with(this)
     .load(new MyGlideUrl(url))
     .into(imageView);123

也就是说,我们需要在load()方法中传入这个自定义的MyGlideUrl对象,而不能再像之前那样直接传入url字符串了。不然的话Glide在内部还是会使用原始的GlideUrl类,而不是我们自定义的MyGlideUrl类。

这样我们就将这个棘手的缓存问题给解决掉了。


  • 2021-01-13 13:36:29

    shortid nodejs短id生成器

    短ID在实际运用中很广泛, 其中比较典型的运用就是短地址。 市面上肯定有不少开源的生成短ID库, 基于node.js的估计也不少。 鉴于本人已然是node.js的脑残粉(本职java开发), 很多业余项目从前端到后端都基于javascript开发, 加上npm和bower的包管理以及grunt的打包工具, 在项目开发过程中体验特别酸爽。 由于当时项目前后端都会用到短ID, 但没找到合适的库同时支持npm和bower的(可能孤陋寡闻). 因此自己乐此不疲地又造了个轮子js-shortid(夷,为什么会说又呢?!). 下面主要介绍它的实现方案, 自认为比较优雅简洁。

  • 2021-01-13 17:23:21

    CREATE TABLE 表名 AS SELECT 语句 快速复制表但是锁表

    注意Table2的主键约束,如果Table2有主键而且不为空,则 field1, field2…中必须包括主键 在执行语句的时候,MySQL是逐行加锁的(扫描一个锁一个),直至锁住所有符合条件的数据,执行完毕才释放锁。所以当业务在进行的时候,切忌使用这种方法。 在RR隔离级别下,还会加行锁和间隙锁

  • 2021-01-13 17:27:04

    Navicat配置mysql数据库用户权限

    用数据库的时候就会遇到有多个用户,分配用户权限的情况,有些用户只读,有些用户可以读写,有些用户只能操作一个或者多个数据库,如何给mysql的用户设置权限,我这里描述一下如何用navicat图形操作分配用户权限

  • 2021-01-14 06:15:04

    通过glide获取图片显示后的真正宽高

    有时候需要获取网络图片的宽高来设置图片显示的大小,很多人会直接利用Glide的加载监听去拿图片的宽高,但是这样拿到的不是图片真正的宽高,而是图片显示在ImageView后的宽高。如下:

  • 2021-01-14 09:38:57

    Chrome插件详细教程

    严格来讲,我们正在说的东西应该叫Chrome扩展(Chrome Extension),真正意义上的Chrome插件是更底层的浏览器功能扩展,可能需要对浏览器源码有一定掌握才有能力去开发。鉴于Chrome插件的叫法已经习惯,本文也全部采用这种叫法,但读者需深知本文所描述的Chrome插件实际上指的是Chrome扩展。

  • 2021-01-14 17:07:51

    chrome.contextMenus.create不出现菜单

    主要原因是,我每次刷新玩,都复制一下右键,然而并没有出现菜单,一度颓废啊,因为demo,还有其他人的文章都是这样的。 哎,后来发现是这样的,我缺少了contexts选项。其实我是成功了,我现在只要不选择文字,直接点右键,菜单已经出现了哦。