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类。

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


  • 2019-05-21 12:35:54

    RecyclerView setHasFixedSize(true)的意义

    设置为true,再调用notifyDataSetChanged(),发现大小重新计算了,看来理解出现错误了。还是再看一下哪些地方用到这个mHasFixedSize吧。

  • 2019-05-21 12:37:34

    RecyclerView 刷新闪烁

    闪烁是 notifyDataSetChange 造成的。由于适配器不知道整个数据集中的哪些内容已经存在,在重新匹配 ViewHolder 时发生的。 当然,遇到这个问题时有去搜索一些答案,看到的很多都是去禁止 RecycleView 的默认动画,可惜这对我没什么用。下面的方法是对我有用的。

  • 2019-05-21 12:43:26

    (重要)RecycleView的缓存机制

    RecycleView的四级缓存是由三个类共同作用完成的,Recycler、RecycledViewPool和ViewCacheExtension。Recycler用于管理已经废弃或者与RecyclerView分离的ViewHolder,这里面有两个重要的成员,为可以看见的屏幕的内部缓存成员mAttachedScrap、mChangedScrap和滑出屏幕外的外部缓存成员mCachedViews二者共同完成ViewHolder的缓存;RecycledViewPool类是用来缓存整体所有的ViewHolder,是对mCachedViews缓存的补充;ViewCacheExtension是扩展内的缓存对象,默认不加载,需实现方法getViewForPositionAndType(Recycler recycler, int position, int type)来实现自己的缓存。接下来对四级缓存一步步介绍。

  • 2019-05-21 12:44:31

    对嵌套RecyclerView的优化

    RecyclerView 是一个更高级的 ListView ,它可以重用view避免额外创建太多的view从而带来流畅的滚动性能。RecyclerView通过叫做 View pool 的东西持有不再可见的 view ,让它可被回收

  • 2019-05-25 14:54:50

    commit your changes or stash them before you can merge

    Your local changes to the following files would be overwritten by merge:         protected/config/main.php Please, commit your changes or stash them before you can merge. --------------------- 作者:陈小峰_iefreer 来源:CSDN 原文:https://blog.csdn.net/iefreer/article/details/7679631 版权声明:本文为博主原创文章,转载请附上博文链接!

  • 2019-05-27 10:43:34

    IntelliJ IDEA中C盘文件过大怎么办

    当我在D:\ 安装完IDEA9.0之后,建立了一个工程,发现C:\Users\Administrator\.IntelliJIdea90 竟然增大到了500+M,并且随着使用在逐渐增大,这样占用系统盘资源是非常让人不爽的,那么如何将其修改到其他路径呢?