美文网首页Android进阶之路Android开发Android开发
Android图片加载的那些事:为什么你的Glide缓存没有起作

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

作者: Carson带你学安卓 | 来源:发表于2021-09-22 11:21 被阅读0次

    前言

    • Glide,该功能非常强大 Android 图片加载开源框架 相信大家并不陌生

      Github截图
    • 正由于他的功能强大,所以它的源码非常复杂,这导致很多人望而却步

    • 本人尝试将 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参数的逻辑

    生成缓存Keyid参数的逻辑为:直接将图片的 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参数作任何处理
    
    

    4. 总结

    本文主要对Glide的图片缓存功能的使用问题进行讲解

    Carson带你学Android开源库系列文章:
    Carson带你学Android:主流开源图片加载库对比(UIL、Picasso、Glide、Fresco)
    Carson带你学Android:主流开源网络请求库对比(Volley、OkHttp、Retrofit)
    Carson带你学Android:网络请求库Retrofit使用教程
    Carson带你学Android:网络请求库Retrofit源码分析
    Carson带你学Android:图片加载库Glide使用教程
    Carson带你学Android:图片加载库Glide源码分析
    Carson带你学Android:V-Layout,淘宝、天猫都在用的UI框架,赶紧用起来吧!


    欢迎关注Carson_Ho的简书

    不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度


    请点赞!因为你的鼓励是我写作的最大动力!

    相关文章

      网友评论

        本文标题:Android图片加载的那些事:为什么你的Glide缓存没有起作

        本文链接:https://www.haomeiwen.com/subject/zgitfxtx.html