Android 玩转Glide4---缓存篇

前言

系列文章专栏: 玩转Glide4

概述

加载相同的网络资源的时候,为了减少流量开销,优化加载速度,我们一般都会选择使用缓存。
Glide作为最优秀的图片加载库,广受大家认可,必然也有着十分优秀的缓存机制。
Glide缓存基于LRU原理实现了两种缓存:

  1. 内存缓存MemoryCache
  2. 硬盘缓存DiskCache

Glide是默认同时开启这两种缓存的。

内存缓存MemoryCache

基于LRU的缓存驱逐算法实现。
默认开启,也可以调用skipMemoryCache(boolean skip)开启或者关闭。

内存缓存的内容并不会保存到手机存储里,而是临时存在一个固定大小的HashMap中。
因此重启App上次的缓存就会失效,需要重新加载。

硬盘缓存DiskCache

4.x硬盘缓存有如下四种缓存策略:

模式说明备注
NONE不缓存任何内容
DATA只缓存原始图片相当于3.x的SOURCE
RESOURCE只缓存转换过后的图片相当于3.x的RESULT
ALL既缓存原始图片,也缓存转换过后的图片
AUTOMATIC默认选项,Glide会根据图片资源智能地选择使用哪一种缓存策略4.x新增

注意:4.x缓存策略与3.x略不同,3.x升级到4.x此处需要注意,尤其是要注意3.x默认的缓存策略是RESULT

调用diskCacheStrategy(@NonNull DiskCacheStrategy strategy)即可配置缓存策略。
只要不是NONE策略,使用Glide加载图片,就会默认将图片缓存到硬盘上。
默认的存储路径是内置存储的沙盒环境的cache/image_manager_disk_cache目录。

数据源DataSource

如果你看过DiskCacheStrategy的代码就知道,Glide判断是否缓存的依据就算判断DataSource

DataSource是一个枚举类:

数据源说明
LOCAL本地文件
REMOTE远程链接
DATA_DISK_CACHE原始硬盘缓存
RESOURCE_DISK_CACHE转换后的硬盘缓存
MEMORY_CACHE内存缓存

通过listener(@Nullable RequestListener<TranscodeType> requestListener)方法,我们就可以在onResourceReady回调中判断加载图片完成时,数据源来自哪。

val optionInto = RequestOptions()
    .transform(CircleCrop(), BlurTransformation())
    .skipMemoryCache(false)
    .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
Glide.with(this)
.load(ConstUrl.ImgOne)
.apply(optionInto)
.listener(object : RequestListener<Drawable> {
    override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
        Log.i(TAG, "onLoadFailed: ")
        return false
    }

    override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
        Log.i(TAG, "onResourceReady: $dataSource")
        return false
    }
})
.into(iv)

如上代码。

测试远程链接
  1. 第一次加载一个新图片,DataSource==REMOTE
  2. 不重启App,再次加载改图片,DataSource==MEMORY_CACHE
  3. 重启App,加载该图片,DATA_DISK_CACHE==REMOTE
  4. 不重启App,再次加载改图片,DataSource==MEMORY_CACHE
  5. 清除App缓存,加载该图片DataSource==REMOTE
测试资源drawable

我们将代码修改为加载drawable: load(R.drawable.image_long)

  1. 第一次加载一个新图片,DataSource==LOCAL
  2. 再后面重新加载该图片,都是DataSource==RESOURCE_DISK_CACHE

缓存签名Key

资源文件

上面测试drawable时,我们知道Glide甚至会缓存资源文件。
我们给上面的R.drawable.image_long换一张图片,然后重启App,你会惊奇的发现仍旧显示的是上一个图片的内容。
这个时候你肯定一脸的怀疑人生,有两个大大的疑问:

  1. 加载资源文件我是不是没有关掉缓存?
  2. Glide加载的资源适配了夜间模式岂不是无效?

毕竟现在日常开发中,适配夜间模式和设计图更换是家常便饭,夜间模式要求同名不说,设计图更换一般也是直接替换而不会去改资源名称。
如果这个时候资源文件加载没变,不是得背大锅?但是不对啊,为什么我开发过程没有发现这个问题呢?
莫慌,让我们看看源码压压惊~

public RequestBuilder<TranscodeType> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
    return loadGeneric(resourceId).apply(signatureOf(AndroidResourceSignature.obtain(context)));
  }

我们再看一下这段代码的注解~

默认情况下,此方法将版本代码和基于夜间模式的签名添加到用于在 Glide 中缓存此资源的缓存键。此签名足以保证最终用户将看到您的 Drawable 的最新版本,但在开发过程中,如果您在每次安装之前不增加您的版本代码,并且您在不更改 Drawable 名称的情况下用不同的数据替换 Drawable,您可能会看到不一致的缓存数据。要解决此问题,请考虑在开发期间通过RequestOptions.diskCacheStrategy(DiskCacheStrategy)使用DiskCacheStrategy.NONE ,并为发布版本重新启用默认DiskCacheStrategy.RESOURCE

哈哈,简单解释:Glide已经非常人性化的做了夜间模式切换,而且资源的缓存也是根据版本号来判断的。

打开AndroidResourceSignature类的源码,版本号&夜间模式判断代码如下:

  @NonNull
  public static Key obtain(@NonNull Context context) {
    Key signature = ApplicationVersionSignature.obtain(context);
    int nightMode =
        context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
    return new AndroidResourceSignature(nightMode, signature);
  }

因此这里也提醒我们如果要使用Glide加载资源文件,资源文件更换后,必须修改版本号;如果确实特殊原因无法修改版本号,就要使用DiskCacheStrategy.NONE关掉缓存。

远程链接

所有同学都知道,Glide判断是否需要缓存的依据就是链接url字符串,如果一个url已经缓存过,就直接使用缓存而不会重新加载。
日常开发中也确实如此,理论且合理的来说,每个不同的图片都应该对应唯一的远程链接,不应有重复链接的现象的出现。
但是理想很美好,现实却不是如此。
以我们项目为例,由于旧系统设计的不合理,用户头像是用一个固定链接+用户id拼接而成。
诚然这样获取用户头像链接很方便,却导致一个头疼的问题:用户修改头像后如何刷新缓存?

说到刷新缓存这就要用到方法:signature(@NonNull Key signature)

设置一些额外的数据混合到内存和磁盘缓存键中,允许调用者更好地控制缓存数据何时失效。

4.x移除了StringSignature新增了ObjectKey,ObjectKey允许我们使用任何类型的变量来控制刷新缓存。
如调用这段代码.signature(ObjectKey(System.currentTimeMillis())),表明我们使用使用时间戳去刷新。
这时即使我们开启了全部缓存,每次加载都是DataSource==REMOTE。

其实使用微信时我们也会发现,微信对方的头像即使更换了也不会实时更新,只有我们点击他的头像,到个人页时才会刷新。
因此针对这种情况我们可以:

  1. Glide第一次加载用户头像时,用户id+时间戳生成一个key,调用signature()
  2. 将上面生成的key保存到SharedPreferences等本地。
  3. 访问特定页面,如用户空间页时就刷新key的值。
  4. 用户自己的头像,则可以在修改头像成功后刷新key。

缓存配置

Glide4.x最新版本4.13.1GlideModule&AppliesOptions已经被弃用,亲测在此进行配置也无法生效,因此升级到最新版本的同学要注意啦。

取而代之的是Glide同其他库一样新增了初始化方法。

class App : Application() {

    override fun onCreate() {
        super.onCreate()
        val builder: GlideBuilder = GlideBuilder()
        //设置内存缓存大小为20mb
        val memoryCacheSize = 1024 * 1024 * 20 // 20mb
        //设置内存缓存大小
        builder.setMemoryCache(LruResourceCache(memoryCacheSize.toLong()))
        //设置硬盘缓存大小为200mb
        val diskCacheSize = 1024 * 1024 * 200 // 200mb
        //根据SD卡是否可用选择是在内部缓存还是SD卡缓存
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            builder.setDiskCache(ExternalPreferredCacheDiskCacheFactory(this, "Glide_images", diskCacheSize.toLong()))
        } else {
            builder.setDiskCache(InternalCacheDiskCacheFactory(this, "Glide_images", diskCacheSize.toLong()))
        }
        Glide.init(this, builder)
    }
}

清除缓存

清除内存缓存

Glide会在onLowMemory时自动调用。

 //必须在主线程上调用
Glide.get(this).clearMemory()
清除硬盘缓存
Thread {
   //此方法应始终在后台线程上调用,因为它是阻塞调用
   Glide.get(this).clearDiskCache()
}.start()

代码

GitHub: https://github.com/DeMonDemo/Glide4Img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值