Glide缓存详解(基于3.X版本)

本文深入剖析Glide框架的缓存机制,包括缓存Key的生成方式、内存缓存和磁盘缓存的工作原理,并提供了自定义磁盘缓存的方法及示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.缓存Key

由上一章节源码分析可知,Glide框架的缓存Key在Engine类中生成。

 

1.截图Engine类在Glide源码位置

 

2.Engine类生成缓存Key

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) {




  EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());



}

也就是使用EngineKeyFactory类创建EngineKey对象。

class EngineKeyFactory {

    @SuppressWarnings("rawtypes")
    public EngineKey buildKey(String id, Key signature, int width, int height, 
    ResourceDecoder cacheDecoder,
            ResourceDecoder sourceDecoder, Transformation transformation, ResourceEncoder encoder,
            ResourceTranscoder transcoder, Encoder sourceEncoder) {
        return new EngineKey(id, signature, width, height, cacheDecoder, sourceDecoder, transformation, encoder,
            transcoder, sourceEncoder);
    }

}

其中,可以看出生成EngineKey对象时,传的参数比较多。比如图片的宽度width和图片的高度height都可以影响EngineKey对象的生成。也就是说我们使用Glide时,即使图片路径一样。但是如果操作了图片的宽高。那么就会生成不同的缓存EngineKey对象

 

 

 

 

 

 

二.内存缓存

1.简介

默认情况下,Glide框架会自动开启内存缓存。也就是说,当我们使用Glide加载了一张图片之后,这张图片就会被缓存到内存当中。只要它没从内存中清除,那么下次使用Glide再加载这张图片都会直接从内存当中读取,而不用重新从网络或硬盘上读取了,这样无疑就可以大幅度提升图片的加载效率。

 

 

2.代码开启关闭

<1> 关闭

Glide.with(this).load(url).skipMemoryCache(true).into(imageView);

<2> 开启 默认就是开启,也就是说只要不操作skipMemoryCache。就是默认开启内存缓存的

Glide.with(this).load(url).skipMemoryCache(false).into(imageView);

 

 

3.源码

DrawableRequestBuilder类的skipMemoryCache方法

@Override
public DrawableRequestBuilder<ModelType> skipMemoryCache(boolean skip) {
    super.skipMemoryCache(skip);
    return this;
}

 

调用GenericRequestBuilder类的skipMemoryCache方法

public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> skipMemoryCache(boolean skip) {
    this.isCacheable = !skip;

    return this;
}

也就是赋值GenericRequestBuilder类的isCacheable变量 此变量默认true也就是开启内存缓存。

private boolean isCacheable = true;

此变量通过本类的obtainRequest方法获取Request对象。

private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
        RequestCoordinator requestCoordinator) {
    return GenericRequest.obtain(

            ...

            isCacheable,

            ...
            );
}

因为此方法返回GenericRequest对象,所以我们下面看一下这个类

 

GenericRequest类的onSizeReady()方法

@Override
public void onSizeReady(int width, int height) {
        
    ...
    loadedFromMemoryCache = true;
    loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,priority, isMemoryCacheable, diskCacheStrategy, this);
    loadedFromMemoryCache = resource != null;

    ...
    
}

 

然后调用Engine类的load方法。

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();
        
    final String id = fetcher.getId();

    //生成缓存EnginKey 也就是Glide的缓存Key
    EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),transcoder, loadProvider.getSourceEncoder());

    //如果内存中缓存到的值 不为空 则使用缓存中的数据回调 并且return
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }

       
    return new LoadStatus(cb, engineJob);
}

 

获取缓存方法loadFromCache方法源码 

传参Key:Glide的缓存Key

传参isMemoryCacheable:是否缓存

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    if (!isMemoryCacheable) {
        return null;
    }

    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
        cached.acquire();
        activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
    }
    return cached;
}

<1> 传参 不使用内存缓存 return null。

<2> 否则继续。

获取EngineResource对象

EngineResource<?> cached = getEngineResourceFromCache(key);

 

然后,将刚刚得到的 EngineResource对象+缓存Key+getReferenceQueue()方法获取的对象 生成一个软引用WeakReference<EngineResource<?>>对象。最后存储在Map中

//声明
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;


//初始化
Engine类构造方法中
this.activeResources = activeResources;


 

软引用类

private static class ResourceWeakReference extends WeakReference<EngineResource<?>> {
    private final Key key;

    public ResourceWeakReference(Key key, EngineResource<?> r, ReferenceQueue<? super EngineResource<?>> q) {
        super(r, q);
        this.key = key;
    }
}

 

获取软引用队列

private ReferenceQueue<EngineResource<?>> getReferenceQueue() {
    if (resourceReferenceQueue == null) {
        resourceReferenceQueue = new ReferenceQueue<EngineResource<?>>();


        //获取MessageQueue
        MessageQueue queue = Looper.myQueue();
        
        //添加IdleHandler
        queue.addIdleHandler(new RefQueueIdleHandler(activeResources, resourceReferenceQueue));
    }
    return resourceReferenceQueue;
}

 

小结1

<1> Glide的内存缓存开始于Engine类load方法。

<2> 存内存缓存时会通过key获取EngineResource对象。获取到缓存图片之后会先将它从缓存中移除,然后再重新将这个缓存图片存储到activeResources当中。activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片。

<3> Glide内存缓存底层用的是LruCache类

 

LruCache+DiskLruCache详解

https://blog.youkuaiyun.com/weixin_37730482/article/details/71191150

 

 

 

 

 

三.磁盘缓存

1.简介

默认情况下,Glide框架会自动开启磁盘缓存。

 

 

2.代码开启关闭

关闭

Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);

即调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能。

<1> DiskCacheStrategy.NONE: 表示不缓存任何内容。
<2> DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
<3> DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
<4> DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。

 

 

3.源码

DrawableRequestBuilder类的diskCacheStrategy()方法

@Override
public DrawableRequestBuilder<ModelType> diskCacheStrategy(DiskCacheStrategy strategy) {
    super.diskCacheStrategy(strategy);
    return this;
}

 

调用

public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType>  diskCacheStrategy(DiskCacheStrategy strategy) {
    this.diskCacheStrategy = strategy;

    return this;
}

 

流程图

 

由流程图到 DecodeJob类loadFromCache方法

private Resource<T> loadFromCache(Key key) throws IOException {
    File cacheFile = diskCacheProvider.getDiskCache().get(key);
    if (cacheFile == null) {
        return null;
    }

    Resource<T> result = null;
    try {
        result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
    } finally {
        if (result == null) {
            diskCacheProvider.getDiskCache().delete(key);
        }
    }
    return result;
}

//磁盘缓存接口 Glide框架中唯一实现类是Engine类的LazyDiskCacheProvider内部静态类
private final DiskCacheProvider diskCacheProvider;

//设置的磁盘缓存策略
private final DiskCacheStrategy diskCacheStrategy;

 

 

小结2

<1> Glide的磁盘缓存底层用的是DiskLruCache类。

LruCache+DiskLruCache详解

https://blog.youkuaiyun.com/weixin_37730482/article/details/71191150

 

 

 

 

 

四.自定义磁盘缓存

 

Key实现类

public class OriginalKey implements Key {

    private final String id;
    private final Key signature;

    public OriginalKey(String id, Key signature) {
        this.id = id;
        this.signature = signature;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        OriginalKey that = (OriginalKey) o;

        if (!id.equals(that.id)) {
            return false;
        }
        if (!signature.equals(that.signature)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = id.hashCode();
        result = 31 * result + signature.hashCode();
        return result;
    }

    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
        try {
            messageDigest.update(id.getBytes(STRING_CHARSET_NAME));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        signature.updateDiskCacheKey(messageDigest);
    }
}


 

Key管理类

public class SafeKeyGenerator {

    private final LruCache<Key, String> loadIdToSafeHash = new LruCache<Key, String>(1000);

    public String getSafeKey(Key key) {
        String safeKey;
        synchronized (loadIdToSafeHash) {
            safeKey = loadIdToSafeHash.get(key);
        }
        if (safeKey == null) {
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
                key.updateDiskCacheKey(messageDigest);
                safeKey = Util.sha256BytesToHex(messageDigest.digest());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            synchronized (loadIdToSafeHash) {
                loadIdToSafeHash.put(key, safeKey);
            }
        }
        return safeKey;
    }
}

 

外部调用类

public class GlideImageUtils {

    private Context context;
    public GlideImageUtils(Context context) {
        this.context = context;
    }

    public File getCacheFile(String id) {
        OriginalKey originalKey = new OriginalKey(id, EmptySignature.obtain());
        SafeKeyGenerator safeKeyGenerator = new SafeKeyGenerator();
        String safeKey = safeKeyGenerator.getSafeKey(originalKey);
        try {
            DiskLruCache diskLruCache = DiskLruCache.open(new File(context.getCacheDir(), DiskCache.Factory.DEFAULT_DISK_CACHE_DIR), 1, 1, DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE);
            DiskLruCache.Value value = diskLruCache.get(safeKey);
            if (value != null) {
                return value.getFile(0);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}


结果 


首次进入页面

D/GlideActivity: file----:null


再次进入页面

D/GlideActivity: file----:/data/user/0/com.wjn.rxdemo/cache/image_manager_disk_cache/2355ef2bc78d689ec683e8db475ce6f36a817b5ad385235ecd94de1a1a3f8d3b.0

 

如果图片的路径后面有参数,说明图片的路径是变化的,但是图片不变。比如

http://url.com/image.jpg?token=d9caa6e02c990b0a

token可能会变,但是图片不变。这样的话要是不处理,就会每一个token对应一个图片。从而一张图片缓存多张图片。

 

由上代码可知

final String id = fetcher.getId();

id其实就是图片的url地址。那么,这里是通过调用fetcher.getId()方法来获取的图片url地址,而我们在上一篇文章中已经知道了,fetcher就是HttpUrlFetcher的实例。

 

getId()方法的源码

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

 由上代码可知,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();
    }
 
    ...
}

 

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

 

使用

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

 

 

 

 

 

 

 

五.Glide框架Application配置

项目Application级别管理Glide缓存

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    /**
     * onLowMemory:系统整体内存较低时调用
     */

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        Glide.get(getApplicationContext()).clearMemory();
        Log.d("MyApplication", "onLowMemory方法执行");
    }

    /**
     * 通知应用程序内存使用情况
     * 每次退出APP时调用
     */

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        if (level == Glide.TRIM_MEMORY_UI_HIDDEN) {
            Glide.get(getApplicationContext()).clearMemory();
        }
        Glide.get(getApplicationContext()).trimMemory(level);
        Log.d("MyApplication", "onTrimMemory方法执行level----:" + level);
    }
}

 

每次退出应用结果

D/MyApplication: onTrimMemory方法执行level----:20

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值