Glide 配置、缓存

Glide 配置

Glide 缓存

大致目录:
//配置
* 设置
  * 应用程序
  * 程序库 (Libraries)
    * 避免在程序库中使用 AppGlideModule

* 应用程序选项
  *内存缓存
  * Bitmap 池
  * 磁盘缓存

* 默认请求选项

* 未捕获异常策略 (UncaughtThrowableStrategy)
  * 日志级别

* 注册组件

* 剖析(Anatomy)一个请求

* 排序组件
  * prepend()
  * append()
  * replace()
  * 添加一个 ModelLoader

* 模块类和注解
  * AppGlideModule
  * @GlideModule
  * 注解处理器

* 冲突

* 清单解析
//缓存
* Glide里的缓存

* 缓存键(Cache Keys)

* 配置缓存
  * 磁盘缓存策略(Disk Cache Strategy)
  * 仅从缓存加载图片
  * 跳过缓存
  * 实现

* 缓存的刷新
  * 定制缓存刷新策略

* 资源管理
  * 内存缓存
    * 永久尺寸调整
    * 暂时尺寸调整
  * 磁盘缓存
    * 永久尺寸修改
    * 清理磁盘缓存

一、配置

1.1 设置

为了让 Glide 正常工作,库和应用程序需要做一些固定的步骤。不过,假如你的库不希望注册额外的组件,则这些初始化不是必须的。

1.1.1 应用程序

应用程序 (Applications) 需要:

  1. 恰当地添加一个 AppGlideModule 实现。

  2. (可选)添加一个或多个 LibraryGlideModule 实现。

  3. 给上述两种实现添加 @GlideModule 注解。

  4. 添加对 Glide 的注解解析器的依赖。

  5. 在 proguard 中,添加对 AppGlideModuleskeep

在 Glide 的 Flickr 示例应用 中,有一个 AppGlideModule 的示例实现:

@GlideModule
public class FlickrGlideModule extends AppGlideModule {
  @Override
  public void registerComponents(Context context, Registry registry) {
    registry.append(Photo.class, InputStream.class, new FlickrModelLoader.Factory());
  }
}

请注意添加对 Glide 的注解和注解解析器的依赖:

compile 'com.github.bumptech.glide:annotations:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

最后,你应该在你的 proguard.cfg 中 keep 住你的 AppGlideModule 实现:

-keep public class  extends com.bumptech.glide.module.AppGlideModule
-keep class com.bumptech.glide.GeneratedAppGlideModuleImpl

1.1.2 程序库 (Libraries)

程序库若不需要注册定制组件,则不需要做任何配置步骤,可以完全跳过这个章节。

程序库如果需要注册定制组件,例如 ModelLoader,可按以下步骤执行:

  1. 添加一个或多个 LibraryGlideModule 实现,以注册新的组件。

  2. 为每个 LibraryGlideModule 实现,添加 @GlideModule 注解。

  3. 添加 Glide 的注解处理器的依赖。

一个 LibraryGlideModule 的例子,在 Glide 的 OkHttp 集成库 中:

@GlideModule
public final class OkHttpLibraryGlideModule extends LibraryGlideModule {
  @Override
  public void registerComponents(Context context, Glide glide, Registry registry) {
    registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
  }
}

使用 GlideModule 注解需要使用 Glide 注解的依赖:

compile 'com.github.bumptech.glide:annotations:4.8.0'
避免在程序库中使用 AppGlideModule

程序库一定不要包含 AppGlideModule 实现。这么做将会阻止依赖该库的任何应用程序管理它们的依赖,或配置诸如 Glide 缓存大小和位置之类的选项。

此外,如果两个程序库都包含 AppGlideModule,应用程序将无法在同时依赖两个库的情况下通过编译,而不得不在二者之中做出取舍。

这确实意味着程序库将无法使用 Glide 的 generated API,但是使用 RequestOptions 加载仍然有效(可以在选项页找到例子)。

1.2 应用程序选项

Glide 允许应用通过 AppGlideModule 实现来完全控制 Glide 的内存和磁盘缓存使用。Glide 试图提供对大部分应用程序合理的默认选项,但对于部分应用,可能就需要定制这些值。在你做任何改变时,请注意测量其结果,避免出现性能的倒退。

1.2.1 内存缓存

默认情况下,Glide使用 LruResourceCache,这是 MemoryCache 接口的一个缺省实现,使用固定大小的内存和 LRU 算法。LruResourceCache 的大小由 Glide 的 MemorySizeCalculator 类来决定,这个类主要关注设备的内存类型,设备 RAM 大小,以及屏幕分辨率。

应用程序可以自定义 MemoryCache 的大小,具体是在它们的 AppGlideModule 中使用 applyOptions(Context, GlideBuilder) 方法配置 MemorySizeCalculator

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
        .setMemoryCacheScreens(2)
        .build();
    builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
  }
}

也可以直接覆写缓存大小:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    int memoryCacheSizeBytes = 1024 * 1024 * 20; // 20mb
    builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
  }
}

甚至可以提供自己的 MemoryCache 实现:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setMemoryCache(new YourAppMemoryCacheImpl());
  }
}

1.2.2 Bitmap 池

Glide 使用 LruBitmapPool 作为默认的 BitmapPoolLruBitmapPool 是一个内存中的固定大小的 BitmapPool,使用 LRU 算法清理。默认大小基于设备的分辨率和密度,同时也考虑内存类和 isLowRamDevice 的返回值。具体的计算通过 Glide 的 MemorySizeCalculator 来完成,与 Glide 的 MemoryCache 的大小检测方法相似。

应用可以在它们的 AppGlideModule 中定制 BitmapPool 的尺寸,使用 applyOptions(Context, GlideBuilder) 方法并配置 MemorySizeCalculator:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
        .setBitmapPoolScreens(3)
        .build();
    builder.setBitmapPool(new LruBitmapPool(calculator.getBitmapPoolSize()));
  }
}

应用也可以直接复写这个池的大小:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    int bitmapPoolSizeBytes = 1024 * 1024 * 30; // 30mb
    builder.setBitmapPool(new LruBitmapPool(bitmapPoolSizeBytes));
  }
}

甚至可以提供 BitmapPool 的完全自定义实现:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setBitmapPool(new YourAppBitmapPoolImpl());
  }
}

1.2.3 磁盘缓存

Glide 使用 DiskLruCacheWrapper 作为默认的磁盘缓存。DiskLruCacheWrapper 是一个使用 LRU 算法的固定大小的磁盘缓存。默认磁盘大小为 250 MB,位置是在应用的缓存文件夹中的一个特定目录。

假如应用程序展示的媒体内容是公开的(从无授权机制的网站上加载,或搜索引擎等),那么应用可以将这个缓存位置改到外部存储:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setDiskCache(new ExternalDiskCacheFactory(context));
  }
}

无论使用内部或外部磁盘缓存,应用程序都可以改变磁盘缓存的大小:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    int diskCacheSizeBytes = 1024  1024  100;  100 MB
    builder.setDiskCache(new InternalDiskCacheFactory(context, diskCacheSizeBytes));
  }
}

应用程序还可以改变缓存文件夹在外存或内存上的名字:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    int diskCacheSizeBytes = 1024  1024  100;  100 MB
    builder.setDiskCache(
        new InternalDiskCacheFactory(context, cacheFolderName, diskCacheSizeBytes));
  }
}

应用程序还可以自行选择 DiskCache 接口的实现,并提供自己的 DiskCache.Factory 来创建缓存。Glide 使用一个工厂接口来在后台线程中打开磁盘缓存,这样方便缓存做诸如检查路径存在性等的 IO 操作而不用触发严格模式

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setDiskCache(new DiskCache.Factory() {
        @Override
        public DiskCache build() {
          return new YourAppCustomDiskCache();
        }
    });
  }
}

1.3 默认请求选项

虽然请求选项通常由每个请求单独指定,你也可以通过 AppGlideModule 应用一个请求选项的集合以作用于你应用中启动的每个加载:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setDefaultRequestOptions(
        new RequestOptions()
          .format(DecodeFormat.RGB_565)
          .disallowHardwareBitmaps());
  }
}

一旦你创建了新的请求,这些选项将通过 GlideBuilder 中的 setDefaultRequestOptions 被应用上。因此,任何单独请求里应用的选项将覆盖 GlideBuilder 里设置的冲突选项。

类似地,RequestManagers 允许你为这个特定的 RequestManager 启动的所有加载请求设置默认的请求选项。 因为每个 Activity 和 Fragment 都拥有自己的 RequestManager,你可以使用 RequestManagerapplyDefaultRequestOptions 方法来设置默认的 RequestOption,并仅作用于一个特定的 Activity 或 Fragment:

Glide.with(fragment)
  .applyDefaultRequestOptions(
      new RequestOptions()
          .format(DecodeFormat.RGB_565)
          .disallowHardwareBitmaps());

RequestManager 还有一个 setDefaultRequestOptions 方法,可以完全替换掉之前设置的任意的默认请求选项,无论它是通过 AppGlideModule 的 GlideBuilder 还是 RequestManager。使用 setDefaultRequestOptions 要小心,因为很容易意外覆盖掉你其他地方设置的重要默认选项。 通常 applyDefaultRequestOptions 更安全,使用起来更直观。

1.4 未捕获异常策略 (UncaughtThrowableStrategy)

在加载图片时假如发生了一个异常 (例如 OOM),Glide 将会使用一个 GlideExecutor.UncaughtThrowableStrategy

默认策略是将异常打印到设备的 LogCat 中。 这个策略从 Glide 4.2.0 起将可被定制。 你可以传入一个磁盘执行器和/或一个 resize 执行器:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    final UncaughtThrowableStrategy myUncaughtThrowableStrategy = new ...
    builder.setDiskCacheExecutor(newDiskCacheExecutor(myUncaughtThrowableStrategy));
    builder.setResizeExecutor(newSourceExecutor(myUncaughtThrowableStrategy));
  }
}

1.4.1 日志级别

你可以使用 setLogLevel (结合 Android 的 Log 定义的值) 来获取格式化日志的子集,包括请求失败时的日志行。通常来说 Log.VERBOSE 将使日志变得更冗杂,Log.ERROR 会让日志更趋向静默,详细可见 javadoc

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    builder.setLogLevel(Log.DEBUG);
  }
}

1.5 注册组件

应用程序和库都可以注册很多组件来扩展 Glide 的功能。可用的组件包括:

  1. ModelLoader 用于加载自定义的 Model(Url, Uri,任意的 POJO)Data(InputStreams, FileDescriptors)

  2. ResourceDecoder 用于对新的 Resources(Drawables, Bitmaps) 或新的 Data 类型 (InputStreams, FileDescriptors) 进行解码。

  3. Encoder 用于向 Glide 的磁盘缓存写 Data (InputStreams, FileDesciptors)

  4. ResourceTranscoder 用于在不同的资源类型之间做转换,例如,从 BitmapResource 转换为 DrawableResource

  5. ResourceEncoder 用于向 Glide 的磁盘缓存写 Resources(BitmapResource, DrawableResource)

组件通过 Registry 类来注册。例如,添加一个 ModelLoader,使其能从自定义的 Model 对象中创建一个 InputStream:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void registerComponents(Context context, Registry registry) {
    registry.append(Photo.class, InputStream.class, new CustomModelLoader.Factory());
  }
}

在一个 GlideModule 里可以注册很多组件。ModelLoaderResourceDecoder 对于同样的参数类型还可以有多种实现。

1.6 剖析(Anatomy)一个请求

被注册组件的集合(包括默认被 Glide 注册的和在 Module 中被注册的),会被用于定义一个加载路径集合。每个加载路径都是从提供给 load 方法的数据模型到 as 方法指定的资源类型的一个逐步演进的过程。一个加载路径(粗略地)由下列步骤组成:

  1. 模型 (Model) -> 数据 (Data) (由模型加载器 (ModelLoader) 处理)

  2. 数据 (Data) -> 资源 (Resource) (由资源解析器 (ResourceDecoder) 处理)

  3. 资源 (Resource) -> 转码后的资源 (Transcoded Resource) (可选;由资源转码器 (ResourceTranscoder) 处理)

编码器 (Encoder) 可以在步骤 2 之前往 Glide 的磁盘缓存中写入数据。资源编码器 (ResourceEncoder) 可以在步骤 3 之前往 Glide 的磁盘缓存写入资源。

当一个请求开始后,Glide 将尝试所有从数据模型到请求的资源类型的可用路径。如果任何一个加载路径成功,这个请求就将成功。只有所有可用加载路径都失败时,这个请求才会失败。

1.7 排序组件

Registry 类中定义了 prepend() , append()replace() 方法,它们可以用于设置 Glide 尝试每个 ModelLoaderResourceDecoder 之间的顺序。对组件进行排序允许你注册一些只处理特定树模型的子集的组件(即只处理特定类型的 Uri,或仅特定类型的图像格式),并可以在后面追加一个捕获所有类型的组件以处理其他情况。

  • prepend()
    假如你的 ModelLoader 或者 ResourceDecoder 在某个地方失败了,这时候你想将已有的数据交由 Glide 的默认行为来处理,可以使用 prepend()prepend() 将确保你的 ModelLoaderResourceDecoder 先于之前注册的其他组件并被首先执行。如果你的 ModelLoader 或者 ResourceDecoder 从其 handles() 方法中返回了一个 false 或失败,所有其他的 ModelLoaderResourceDecoder 将以它们被注册的顺序执行,一次一个,作为一种回退方案。

  • append()
    要处理新的数据类型或提供一个到 Glide 默认行为的回退,使用 append()append() 将确保你的 ModelLoaderResourceDecoder 仅在 Glide 的默认组件被尝试后才会被调用。 如果你正在尝试处理 Glide 的默认组件能处理的某些子类型 (例如一些特定的 Uri 授权或子类型),你可能需要使用 prepend() 来确保 Glide 的默认组件不会在你的定制组件之前加载。

  • replace()
    要完全替换 Glide 的默认行为并确保它绝不运行,请使用 replace()replace() 将移除所有处理给定模型和数据类的 ModelLoaders,并添加你的 ModelLoader 来代替。replace() 在使用库 (例如 OkHttp 或 Volley) 替换掉 Glide 的网络逻辑时尤其有用,这种时候你会希望确保仅 OkHttp 或 Volley 被调用。

1.7.1 添加一个 ModelLoader

举个例子,添加一个 ModelLoader,它从一个新的自定义 Model 对象中建立一个 InputStream

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void registerComponents(Context context, Glide glide, Registry registry) {
    registry.append(Photo.class, InputStream.class, new CustomModelLoader.Factory());
  }
}

在这里,append() 可以被安全地使用,因为 Photo.class 是一个你的应用定制的模型对象,所以你知道 Glide 的默认行为中并没有你需要替换的东西。

相反,如果要处理一种新的 BaseGlideUrlLoader 中的 String Url 类型,你应该使用 prepend() 以使你的 ModelLoader 在 Glide 对 Strings 的默认 ModelLoaders 之前运行:

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void registerComponents(Context context, Glide glide, Registry registry) {
    registry.prepend(String.class, InputStream.class, new CustomUrlModelLoader.Factory());
  }
}

最后,如果要完全移除和替换 Glide 对某种特定类型的默认处理,例如一个网络库,你应该使用 replace()

@GlideModule
public class YourAppGlideModule extends AppGlideModule {
  @Override
  public void registerComponents(Context context, Glide glide, Registry registry) {
    registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
  }
}

1.8 模块类和注解

Glide v4 依赖于两种类,AppGlideModule 与 LibraryGlideModule ,以配置 Glide 单例。这两种类都允许用于注册额外的组件,例如 ModelLoaders , ResourceDecoders 等。但只有 AppGlideModule 被允许配置应用特定的设置项,比如缓存实现和缓存大小。

1.8.1 AppGlideModule

所有应用都必须添加一个 AppGlideModule 实现,即使应用并没有改变任何附加设置项,也没有实现 AppGlideModule 中的任何方法。 AppGlideModule 实现是一个信号,它会让 Glide 的注解解析器生成一个单一的所有已发现的 LibraryGlideModules 的联合类。

对于一个特定的应用,只能存在一个 AppGlideModule 实现(超过一个会在编译时报错)。因此,程序库不能提供 AppGlideModule 实现。

1.8.2 @GlideModule

为了让 Glide 正确地发现 AppGlideModule 和 LibraryGlideModule 的实现类,它们的所有实现都必须使用 @GlideModule 注解来标记。这个注解将允许 Glide 的注解解析器在编译时去发现所有的实现类。

注解处理器

另外,为了发现 AppGlideModule 和 LibraryGlideModules,所有的库和应用还必须包含一个Glide的注解解析器的依赖。

1.9 冲突

应用程序可能依赖多个程序库,而它们每一个都可能包含一个或更多的 LibraryGlideModules 。在极端情况下,这些 LibraryGlideModules 可能定义了相互冲突的选项,或者包含了应用程序希望避免的行为。应用程序可以通过给他们的 AppGlideModule 添加一个 @Excludes 注解来解决这种冲突,或避免不需要的依赖。

例如,如果你依赖了一个库,它有一个 LibraryGlideModule 叫做com.example.unwanted.GlideModule,而你不想要它:

@Excludes(com.example.unwanted.GlideModule)
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }

你也可以排除多个模块:

@Excludes({com.example.unwanted.GlideModule, com.example.conflicing.GlideModule})
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }

@Excludes 注解不仅可用于排除 LibraryGlideModules 。如果你还在从 Glide v3 到新版本的迁移过程中,你还可以用它来排除旧的,废弃的 GlideModule 实现。

1.10 清单解析

为了维持对 Glide v3 的 GlideModules 的向后兼容性,Glide 仍然会解析应用程序和所有被包含的库中的 AndroidManifest.xml 文件,并包含在这些清单中列出的旧 GlideModules 模块类。

如果你已经迁移到 Glide v4 的 AppGlideModule 和 LibraryGlideModule ,你可以完全禁用清单解析。这样可以改善 Glide 的初始启动时间,并避免尝试解析元数据时的一些潜在问题。要禁用清单解析,请在你的 AppGlideModule 实现中复写 isManifestParsingEnabled() 方法:

@GlideModule
public final class MyAppGlideModule extends AppGlideModule {
  @Override
  public boolean isManifestParsingEnabled() {
    return false;
  }
}

二、缓存

2.1 Glide 里的缓存

默认情况下,Glide 会在开始一个新的图片请求之前检查以下多级的缓存:

1. 活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片?

2. 内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中?

3. 资源类型(Resource) - 该图片是否之前曾被解码、转换并写入过磁盘缓存?

4. 数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?

前两步检查图片是否在内存中,如果是则直接返回图片。后两步则检查图片是否在磁盘上,以便快速且异步地返回图片。

如果四个步骤都未能找到图片,则 Glide 会返回到原始资源以取回数据(原始文件,Uri, Url 等)。

关于 Glide 缓存的默认大小与它们在磁盘上的位置的更多细节,请看第一大点。

2.2 缓存键 (Cache Keys)

在 Glide v4 里,所有缓存键都包含至少两个元素:

  1. 请求加载的 model (File, Url, Url)

  2. 一个可选的签名 (Signature)

另外,活动资源,内存缓存,资源磁盘缓存的缓存键还包含一些其他数据,包括:

  1. 宽度和高度

  2. 可选的变换 (Transformation)

  3. 额外添加的任何选项 (Options)

  4. 请求的数据类型 (Bitmap, GIF, 或其他)

活动资源和内存缓存使用的键还和磁盘资源缓存略有不同,以适应内存选项 (Options),比如影响 Bitmap 配置的选项或其他解码时才会用到的参数。

为了生成磁盘缓存上的缓存键名称,以上的每个元素会被哈希化以创建一个单独的字符串键名,并在随后作为磁盘缓存上的文件名使用。

2.3 配置缓存

Glide 提供一系列的选项,以允许你选择加载请求与 Glide 缓存如何交互。

2.3.1 磁盘缓存策略(Disk Cache Strategy)

DiskCacheStrategy 可被 diskCacheStrategy 方法应用到每一个单独的请求。目前支持的策略允许你阻止加载过程使用或写入磁盘缓存,选择性地仅缓存无修改的原生数据,或仅缓存变换过的缩略图,或是兼而有之。

默认的策略叫做 AUTOMATIC,它会尝试对本地和远程图片使用最佳的策略。当你加载远程数据(比如,从 URL 下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过 (比如,变换,裁剪) 的原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。

指定 DiskCacheStrategy 非常容易:

GlideApp.with(fragment)
  .load(url)
  .diskCacheStrategy(DiskCacheStrategy.ALL)
  .into(imageView);

2.3.2 仅从缓存加载图片

某些情形下,你可能希望只要图片不在缓存中则加载直接失败(比如省流量模式)。如果要完成这个目标,你可以在单个请求的基础上使用 onlyRetrieveFromCache 方法:

GlideApp.with(fragment)
  .load(url)
  .onlyRetrieveFromCache(true)
  .into(imageView);

如果图片在内存缓存或在磁盘缓存中,它会被展示出来。否则只要这个选项被设置为 true,这次加载会视同失败。

2.3.3 跳过缓存

如果你想确保一个特定的请求跳过磁盘和/或内存缓存(比如,图片验证码),Glide 也提供了一些替代方案。

仅跳过内存缓存,请使用 skipMemoryCache()

GlideApp.with(fragment)
  .load(url)
  .skipMemoryCache(true)
  .into(view);

仅跳过磁盘缓存,请使用 DiskCacheStrategy.NONE:

GlideApp.with(fragment)
  .load(url)
  .diskCacheStrategy(DiskCacheStrategy.NONE)
  .into(view);

这两个选项可以同时使用:

GlideApp.with(fragment)
  .load(url)
  .diskCacheStrategy(DiskCacheStrategy.NONE)
  .skipMemoryCache(true)
  .into(view);

虽然提供了这些办法让你跳过缓存,但你通常应该不会想这么做。从缓存中加载一个图片,要比拉取 - 解码 - 转换成一张新图片的完整流程快得多。

2.3.4 实现

如果内置的选项不满足你的需求,你也可以编写你自己的 DiskCache 实现。

2.4 缓存的刷新

因为磁盘缓存使用的是哈希键,所以并没有一个比较好的方式来简单地删除某个特定 url 或文件路径对应的所有缓存文件。如果你只允许加载或缓存原始图片的话,问题可能会变得更简单,但因为 Glide 还会缓存缩略图和提供多种变换 (transformation),它们中的任何一个都会导致在缓存中创建一个新的文件,而要跟踪和删除一个图片的所有版本无疑是困难的。

在实践中,使缓存文件无效的最佳方式是在内容发生变化时(url,uri,文件路径等)更改你的标识符。

2.4.1 定制缓存刷新策略

因为通常改变标识符比较困难或者根本不可能,所以 Glide 也提供了 签名 API 来混合(你可以控制的)额外数据到你的缓存键中。签名 (signature) 适用于媒体内容,也适用于你可以自行维护的一些版本元数据。

  • MediaStore 内容 - 对于媒体存储内容,你可以使用 Glide 的 MediaStoreSignature 类作为你的签名。MediaStoreSignature 允许你混入修改时间、MIME 类型,以及 item 的方向到缓存键中。这三个属性能够可靠地捕获对图片的编辑和更新,这可以允许你缓存媒体存储的缩略图。

  • 文件 - 你可以使用 ObjectKey 来混入文件的修改日期。

  • Url - 尽管最好的让 url 失效的办法是让 server 保证在内容变更时对 URL 做出改变,你仍然可以使用 ObjectKey 来混入任意数据(比如版本号)。

将签名传入加载请求很简单:

GlideApp.with(yourFragment)
    .load(yourFileDataModel)
    .signature(new ObjectKey(yourVersionMetadata))
    .into(yourImageView);

媒体存储签名对于 MediaStore 数据来说也很直接:

GlideApp.with(fragment)
    .load(mediaStoreUri)
    .signature(new MediaStoreSignature(mimeType, dateModified, orientation))
    .into(view);

你还可以定义你自己的签名,只要实现 Key 接口就好。请确保正确地实现 equals(), hashCode()updateDiskCacheKey() 方法:

public class IntegerVersionSignature implements Key {
    private int currentVersion;

    public IntegerVersionSignature(int currentVersion) {
         this.currentVersion = currentVersion;
    }
   
    @Override
    public boolean equals(Object o) {
        if (o instanceof IntegerVersionSignature) {
            IntegerVersionSignature other = (IntegerVersionSignature) o;
            return currentVersion = other.currentVersion;
        }
        return false;
    }
 
    @Override
    public int hashCode() {
        return currentVersion;
    }

    @Override
    public void updateDiskCacheKey(MessageDigest md) {
        messageDigest.update(ByteBuffer.allocate(Integer.SIZE).putInt(signature).array());
    }
}

请记住,为了避免降低性能,你需要在后台批量加载任何版本元数据,以便在要加载图像时即已处于可用状态。

如果这些努力都无法奏效,你不能更改标识符,也不能跟踪任何合理的版本元数据的情况下,也可以使用 diskCacheStrategy()DiskCacheStrategy.NONE 来完全禁用磁盘缓存。

2.5 资源管理

Glide 的磁盘和内存缓存都是 LRU ,这意味着在达到使用限制或持续接近限制值之前,它们将占用持续增加的内存或磁盘空间。为了增加额外的灵活性,Glide 提供了一些额外的方式来让你可以管理你的应用使用的资源。

请记住,更大的内存缓存、位图池和磁盘缓存通常能提供更好的性能,或者至少在同等级别。如果你改变了缓存的大小,你应该小心地测量一下你改动之前和之后的性能对比,以确保你的修改带来的性价比是可以接受的。

2.5.1 内存缓存

默认情况下 Glide 的内存缓存和 BitmapPool 会响应 ComponentCallback2,并根据 Android framework 提供的级别自动清理内容。 因此你通常不需要尝试动态监视或清理你的缓存或 BitmapPool。然而,如果必要的话,Glide 确实提供了几个手动选项。

永久尺寸调整

要改变你应用中 Glide 的可用 RAM 大小,请看第一大点。

暂时尺寸调整

要在你应用的特定部分暂时允许 Glide 使用更多或更少的内存,你可以使用 setMemoryCategory

// This method must be called on the main thread.
Glide.get(context).clearMemory();

清理所有内存并非特别经济,并且应该尽可能避免,以避免出现抖动和增加加载时间。

2.5.2 磁盘缓存

Glide 在运行时仅提供对磁盘缓存的有限控制,但是其大小和配置可以在 AppGlideModule 中改变。

永久尺寸修改

要改变你应用中 Glide 可用的 sdcard 可用空间,请看第一大点。

清理磁盘缓存

要尝试清理所有磁盘缓存条目,你可以使用 clearDiskCache

new AsyncTask<Void, Void, Void> {
  @Override
  protected Void doInBackground(Void... params) {
    // This method must be called on a background thread.
    Glide.get(applicationContext).clearDiskCache();
    return null;
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容