Glide v4 知识点总结

来源:互联网 发布:圆形玻璃定制淘宝 编辑:程序博客网 时间:2024/05/18 00:55

性能提示

Android中的动画代价是比较大的,尤其是同时开始大量动画的时候。 交叉淡入和其他涉及 alpha 变化的动画显得尤其昂贵。 此外,动画通常比图片解码本身还要耗时。在列表和网格中滥用动画可能会让图像的加载显得缓慢而卡顿。为了提升性能,请在使用 Glide 向 ListView , GridView, 或 RecyclerView 加载图片时考虑避免使用动画,尤其是大多数情况下,你希望图片被尽快缓存和加载的时候。作为替代方案,请考虑预加载,这样当用户滑动到具体的 item 的时候,图片已经在内存中了。


在 ListView 和 RecyclerView 中的使用

在 ListView 或 RecyclerView 中加载图片的代码和在单独的 View 中加载完全一样。Glide 已经自动处理了 View 的复用和请求的取消:

@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {    String url = urls.get(position);    Glide.with(fragment)        .load(url)        .into(holder.imageView);}

对 url 进行 null 检验并不是必须的,如果 url 为 null,Glide 会清空 View 的内容,或者显示 placeholder Drawable 或 fallback Drawable 的内容。

Glide 唯一的要求是,对于任何可复用的 View 或 Target ,如果它们在之前的位置上,用 Glide 进行过加载操作,那么在新的位置上要去执行一个新的加载操作,或调用 clear() API 停止 Glide 的工作。

@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {    if (isImagePosition(position)) {        String url = urls.get(position);        Glide.with(fragment)            .load(url)            .into(holder.imageView);    } else {        Glide.with(fragment).clear(holder.imageView);        holder.imageView.setImageDrawable(specialDrawable);    }}

对 View 调用 clear() 或 into(View),表明在此之前的加载操作会被取消,并且在方法调用完成后,Glide 不会改变 view 的内容。如果你忘记调用 clear(),而又没有开启新的加载操作,那么就会出现这种情况,你已经为一个 view 设置好了一个 Drawable,但该 view 在之前的位置上使用 Glide 进行过加载图片的操作,Glide 加载完毕后可能会将这个 view 改回成原来的内容。

这里的代码以 RecyclerView 的使用为例,但规则同样适用于 ListView。


占位符

类型

  • 占位符(Placeholder)
  • 错误符(Error)
  • 后备回调符(Fallback)

- 占位符(Placeholder)

占位符是当请求正在执行时被展示的 Drawable 。当请求成功完成时,占位符会被请求到的资源替换。如果被请求的资源是从内存中加载出来的,那么占位符可能根本不会被显示。如果请求失败并且没有设置 error Drawable ,则占位符将被持续展示。类似地,如果请求的url/model为 null ,并且 error Drawable 和 fallback 都没有设置,那么占位符也会继续显示。

使用 generated API :

GlideApp.with(fragment)  .load(url)  .placeholder(R.drawable.placeholder)  .into(view);

Or:

GlideApp.with(fragment)  .load(url)  .placeholder(new ColorDrawable(Color.BLACK))  .into(view);

- 错误符(Error)

error Drawable 在请求永久性失败时展示。error Drawable 同样也在请求的url/model为 null ,且并没有设置 fallback Drawable 时展示。

With the generated API:

GlideApp.with(fragment)  .load(url)  .error(R.drawable.error)  .into(view);

Or:

GlideApp.with(fragment)  .load(url)  .error(new ColorDrawable(Color.RED))  .into(view);

- 后备回调符(Fallback)

fallback Drawable 在请求的url/model为 null 时展示。设计 fallback Drawable 的主要目的是允许用户指示 null 是否为可接受的正常情况。例如,一个 null 的个人资料 url 可能暗示这个用户没有设置头像,因此应该使用默认头像。然而,null 也可能表明这个元数据根本就是不合法的,或者取不到。 默认情况下Glide将 null 作为错误处理,所以可以接受 null 的应用应当显式地设置一个 fallback Drawable 。

使用 generated API:

GlideApp.with(fragment)  .load(url)  .fallback(R.drawable.fallback)  .into(view);

Or:

GlideApp.with(fragment)  .load(url)  .fallback(new ColorDrawable(Color.GREY))  .into(view);

Generated API

使用

  1. 在 Application 模块中包含一个 AppGlideModule 的实现:
@GlideModulepublic final class MyAppGlideModule extends AppGlideModule {}

你不必去重写 AppGlideModule 中的任何一个方法。子类中完全可以不用写任何东西,它只需要继承 AppGlideModule 并且添加 @GlideModule 注解。

AppGlideModule 的实现必须使用 @GlideModule 注解标记。如果注解不存在,该 module 将不会被 Glide 发现,并且在日志中收到一条带有 Glide tag 的警告,表示 module 未找到。

  1. 使用 Generated API

Generated API 默认名为 GlideApp ,与 Application 模块中 AppGlideModule的子类包名相同。在 Application 模块中将 Glide.with() 替换为 GlideApp.with(),即可使用该 API 去完成加载工作:

GlideApp.with(fragment)
.load(myUrl)
.placeholder(R.drawable.placeholder)
.fitCenter()
.into(imageView);
与 Glide.with() 不同,诸如 fitCenter() 和 placeholder() 等选项在 Builder 中直接可用,并不需要再传入单独的 RequestOptions 对象。

  1. 注意

Android Studio 在大多数时候都可以正确地处理注解处理器 (annotation processor) 和 generated API。然而,当你第一次添加你的 AppGlideModule 或做了某些类型的修改后,你可能需要重新构建 (rebuild) 你的项目。 无论何时,如果你发现 API 没有被 import ,或看起来已经过期,你可以通过以下方法重新构建:

  1. 打开 Build 菜单;
  2. 点击 Rebuild Project。

GlideExtension

Glide Generated API 可在 Application 和 Library 中被扩展。扩展使用被注解的静态方法来添加新的选项、修改现有选项、甚至添加额外的类型支持。

@GlideExtension 注解用于标识一个扩展 Glide API 的类。任何扩展 Glide API 的类都必须使用这个注解来标记,否则其中被注解的方法就会被忽略。

被 @GlideExtension 注解的类应以工具类的思维编写。这种类应该有一个私有的、空的构造方法,应为 final 类型,并且仅包含静态方法。被注解的类可以含有静态变量,可以引用其他的类或对象。

在 Application 模块中可以根据需求实现任意多个被 @GlideExtension 注解的类,在 Library 模块中同样如此。当 AppGlideModule 被发现时,所有有效的 Glide 扩展类 会被合并,所有的选项在 API 中均可以被调用。合并冲突会导致 Glide 的 Annotation Processor 抛出编译错误。

被 @GlideExtention 注解的类有两种扩展方式:

  1. GlideOption - 为 RequestOptions 添加一个自定义的选项。
  2. GlideType - 添加对新的资源类型的支持(GIF,SVG 等等)。
    GlideOption

用 @GlideOption

注解的静态方法用于扩展 RequestOptions 。GlideOption 可以:

定义一个在 Application 模块中频繁使用的选项集合。
创建新的选项,通常与 Glide 的 Option 类一起使用。
要定义一个选项集合,你可以这么写:

@GlideExtensionpublic class MyAppExtension {  // Size of mini thumb in pixels.  private static final int MINI_THUMB_SIZE = 100;  private MyAppExtension() { } // utility class  @GlideOption  public static void miniThumb(RequestOptions options) {    options      .fitCenter()      .override(MINI_THUMB_SIZE);  }

这将会在 RequestOptions 的子类中生成一个方法,类似这样:

public class GlideOptions extends RequestOptions {  public GlideOptions miniThumb() {    MyAppExtension.miniThumb(this);  }  ...}

你可以为方法任意添加参数,但要保证第一个参数为 RequestOptions。

@GlideOptionpublic static void miniThumb(RequestOptions options, int size) {  options    .fitCenter()    .override(size);}

在自动生成的方法中新添的参数同样被加了进来:

public GlideOptions miniThumb(int size) {  MyAppExtension.miniThumb(this);}

之后你就可以使用生成的 GlideApp 类调用你的自定义方法:

GlideApp.with(fragment)   .load(url)   .miniThumb(thumbnailSize)   .into(imageView);

使用 @GlideOption 标记的方法应该为静态方法,并且返回值为空。请注意,这些生成的方法在一般的 Glide 和 RequestOptions 类里不可用。

GlideType

被 @GlideType 注解的静态方法用于扩展 RequestManager 。被 @GlideType 注解的方法允许你添加对新的资源类型的支持,包括指定默认选项。

例如,为添加对 GIF 的支持,你可以添加一个被 @GlideType 注解的方法:

@GlideExtensionpublic class MyAppExtension {  private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();  @GlideType(GifDrawable.class)  public static void asGif(RequestBuilder<GifDrawable> requestBuilder) {    requestBuilder      .transition(new DrawableTransitionOptions())      .apply(DECODE_TYPE_GIF);  }}

这样会生成一个包含对应方法的 RequestManager :

public class GlideRequests extends RequesetManager {  public RequestBuilder<GifDrawable> asGif() {    RequestBuilder<GifDrawable> builder = as(GifDrawable.class);    MyAppExtension.asGif(builder);    return builder;  }  ...}

之后你可以使用生成的 GlideApp 类调用你的自定义类型:

GlideApp.with(fragment)  .asGif()  .load(url)  .into(imageView);

被 @GlideType 标记的方法必须使用 RequestBuilder 作为其第一个参数,这里的泛型 对应 @GlideType 注解中传入的类。该方法应为静态方法,且返回值为空。方法必须定义在一个被 @GlideExtension 注解标记的类中。


请求选项

Glide中的大部分设置项都可以通过 RequestOptions 类和 apply() 方法来应用到程序中。

使用 request options 可以实现(包括但不限于):

  • 占位符(Placeholders)
  • 转换(Transformations)
  • 缓存策略(Caching Strategies)
  • 组件特有的设置项,例如编码质量,或Bitmap的解码配置等。

过渡选项

TransitionOptions 用于决定你的加载完成时会发生什么。

使用 TransitionOption 可以应用以下变换:

  • View淡入
  • 与占位符交叉淡入
  • 或者什么都不发生

RequestBuilder

RequestBuilder 是Glide中请求的骨架,负责携带请求的url和你的设置项来开始一个新的加载过程。

使用 RequestBuilder 可以指定:

  • 你想加载的资源类型(Bitmap, Drawable, 或其他)
  • 你要加载的资源地址(url/model)
  • 你想最终加载到的View
  • 任何你想应用的(一个或多个)RequestOption 对象
  • 任何你想应用的(一个或多个)TransitionOption 对象
  • 任何你想加载的缩略图 thumbnail()

选择资源类型

RequestBuilders 是特定于它们将要加载的资源类型的。默认情况下你会得到一个 Drawable RequestBuilder ,但你可以使用 as… 系列方法来改变请求类型。例如,如果你调用了 asBitmap() ,你就将获得一个 BitmapRequestBuilder 对象,而不是默认的 Drawable RequestBuilder。

RequestBuilder<Bitmap> requestBuilder = Glide.with(fragment).asBitmap();

RequestOptions

  • 可以使用 apply() 方法应用 RequestOptions ;
  • 使用 transition() 方法应用 TransitionOptions 。
RequestBuilder<Drawable> requestBuilder = Glide.with(fragment);requestBuilder.apply(requestOptions);requestBuilder.transition(transitionOptions);

RequestBuilder 也可以被复用于开始多个请求:

RequestBuilder<Drawable> requestBuilder =        Glide.with(fragment)            .asDrawable()            .apply(requestOptions);for (int i = 0; i < numViews; i++) {   ImageView view = viewGroup.getChildAt(i);   String url = urls.get(i);   requestBuilder.load(url).into(view);}

缩略图 (Thumbnail) 请求

Glide 的 thumbnail API 允许你指定一个 RequestBuilder 以与你的主请求并行启动。缩略图 会在主请求加载过程中展示。如果主请求在缩略图请求之前完成,则缩略图请求中的图像将不会被展示。[thumbnail] API 允许你简单快速地加载图像的低分辨率版本,并且同时加载图像的无损版本,这可以减少用户盯着加载指示器 【例如进度条–译者注)】 的时间。

thumbnail() API 对本地和远程图片都适用,尤其是当低分辨率缩略图存在于 Glide 的磁盘缓存时,它们将很快被加载出来。

thumbnail API 使用起来相对简单:

Glide.with(fragment)  .load(url)  .thumbnail(Glide.with(fragment)    .load(thumbnailUrl))  .into(imageView);

只要你的 thumbnailUrl 指向的图片比你的主 url 的分辨率更低,它将会很好地工作。相当数量的加载 API 提供了不同的指定图片尺寸的方法,它们尤其适用于 thumbnail API。

如果你仅仅想加载一个本地图像,或者你只有一个单独的远程 URL, 你仍然可以从缩略图 API 受益。请使用 Glide 的 override 或 sizeMultiplier API 来强制 Glide 在缩略图请求中加载一个低分辨率图像:

int thumbnailSize = ...;Glide.with(fragment)  .load(localUri)  .thumbnail(Glide.with(fragment)    .load(localUri)    .override(thumbnailSize))  .into(view);

thumbnail 方法有一个简化版本,它只需要一个 sizeMultiplier 参数。如果你只是想为你的加载相同的图片,但尺寸为 View 或 Target 的某个百分比的话特别有用:

Glide.with(fragment)  .load(localUri)  .thumbnail(/*sizeMultiplier=*/ 0.25f)  .into(imageView);

在失败时开始新的请求

从 Glide 4.3.0 开始,你现在可以使用 error API 来指定一个 RequestBuilder,以在主请求失败时开始一次新的加载。例如,在请求 primaryUrl 失败后加载 fallbackUrl:

Glide.with(fragment)  .load(primaryUrl)  .error(Glide.with(fragment)      .load(fallbackUrl))  .into(imageView);

如果主请求成功完成,这个error RequestBuilder 将不会被启动。如果你同时指定了一个 thumbnail 和一个 error RequestBuilder,则这个后备的 RequestBuilder 将在主请求失败时启动,即使缩略图请求成功也是如此。


目标尺寸测量

默认情况下,Glide 使用 Target 通过 getSize() 方法提供的尺寸作为请求的目标尺寸。这种设计允许 Glide 可以选取合适的 url ,降低采样率,剪裁和转换图像,从而最小化内存的使用,并确保加载尽可能快。

最简单的实现getSize()的方法可能是直接调用库提供的回调:

定制目标

@Overridepublic void getSize(SizeReadyCallback cb) {  cb.onSizeReady(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);}

你也可以向你的 Target 的构造函数传入一个尺寸,并在这个回调中提供这个尺寸:

public class CustomTarget<T> implements Target<T> {  private final int width;  private final int height;  public CustomTarget(int width, int height) {    this.width = width;    this.height = height;  }  ...  @Override  public void getSize(SizeReadyCallback cb) {    cb.onSizeReady(width, height);  }}

View目标

ViewTarget 通过检查 View 的属性和/或使用一个 OnPreDrawListener 在 View 绘制之前直接测量尺寸来实现 getSize() 方法。我们使用以下逻辑:

  • 如果 View 的的宽或高被设置为 > 0,则使用这个尺寸
  • 如果 View 的宽或高被设置为 WRAP_CONTENT ,则使用屏幕的宽或高
  • 如果仍然至少有一个维度 <= 0 并且不是 WRAP_CONTENT ,添加一个 OnPreDrawListener 监听布局。
    ## WRAP_CONTENT
    请注意,Glide 可能对 WRAP_CONTENT 处理不是特别好。这是因为我们很难知道用户的实际意图,尤其是还请求了变换(Transformations)的时候。

我们可以将 WRAP_CONTENT 视为用户请求原始的未修改的图像,但这么做会有在加载超大图像时导致 OutOfMemoryException 的风险。另外,尤其是因为View通常不会超出屏幕,Android Framework 最后可能会将我们设法成功加载的任意高分辨率图像给缩小(downscaling)。

使用屏幕尺寸,至少允许我们缩小超大的图片,而又不完全忽略用户的原始请求意图。

尺寸与性能

一般来说,在加载图片的 View 被设置了显式的dp尺寸的情况下,Glide 提供了最快、最可预测的结果。在不可能获得明确的 dp 尺寸时,Glide 也通过使用 OnPreDrawListeners 为 layout weight, MATCH_PARENT 以及其他相对尺寸提供了相当鲁棒的支持。如果这些都不奏效,Glide 也为 WRAP_CONTENT 提供了应该较为合理的行为。

其他情况

在任何情况下,如果 Glide 看起来获取了错误的 View 尺寸,你都可以手动覆盖来纠正它。你可以选择扩展 ViewTarget 实现你自己的逻辑,或者使用 RequestOption 里的方法。


过渡

标准行为

Glide 提供了很多的过渡效果,用户可以手动地应用于每个请求。Glide 的内置过渡以一致的方式运行,并且将根据加载图像的位置在某些情况下避免运行。

在 Glide 中,图像可能从四个地方中的任何一个位置加载出来:

  • Glide 的内存缓存
  • Glide 的磁盘缓存
  • 设备本地可用的一个源文件或 Uri
  • 仅远程可用的一个源 Url 或 Uri
    如果图像从 Glide 的内存缓存中加载出来,Glide 的内置过渡将不会执行。然而,在另外三种场景下,Glide 的内置过渡都会被执行。

常见错误

  • 对占位符和透明图片交叉淡入

Glide 的默认交叉淡入(cross fade)效果使用了 TransitionDrawable 。它提供两种动画模式,由 setCrossFadeEnabled() 控制。当交叉淡入被禁用时,正在过渡的图片会在原先显示的图像上面淡入。当交叉淡入被启用时,原先显示的图片会从不透明过渡到透明,而正在过渡的图片则会从透明变为不透明。

在 Glide 中,我们默认禁用了交叉淡入,这样通常看起来要好看一些。实际的交叉淡入,如上所述对两个图片同时改变 alpha 值,通常会在过渡的中间造成一个短暂的白色闪屏,这个时候两个图片都是部分不透明的。

不幸的是,虽然禁用交叉淡入通常是一个比较好的默认行为,当待加载的图片包含透明像素时仍然可能造成问题。当占位符比实际加载的图片要大,或者图片部分为透明时,禁用交叉淡入会导致动画完成后占位符在图片后面仍然可见。如果你在加载透明图片时使用了占位符,你可以启用交叉淡入,具体办法是调整 DrawableCrossFadeFactory 里的参数并将结果传到 transition() 中。

  • 在多个请求间交叉淡入

Transitions 并不能让你在不同请求中加载的两个图像之间做过渡。当新的加载被应用到 View 或 Target (查看 Target的文档 )上时,Glide 默认会取消任何已经存在的请求。因此,如果你想加载连个个不同的图片并在它们之间做动画,你无法直接通过 Glide 来完成。等待第一个加载完成并在 View 外持有这个 Bitmap 或 Drawable ,然后开始新的加载并手动在这两者之间做动画,诸如此类的策略看起来有效,但是实际上不安全,并可能导致程序崩溃或图像错误。

相反,最简单的办法是使用包含两个 ImageView 的 ViewSwitcher 来完成。将第一张图片加载到 getNextView() 的返回值里面,然后将第二张图片加载到 getNextView() 的下一个返回值中,并使用一个 RequestListener 在第二张图片加载完成时调用 showNext() 。为了更好地控制,你也可以使用 Android开发者文档 指出的策略。但要记住与 ViewSwitcher 一样,仅在第二次图像加载完成后才开始交叉淡入淡出。

定制过渡

如果要定义一个自定义的过渡动画,你需要完成以下两个步骤:

  • 实现 TransitionFactory .
  • 使用 DrawableTransitionOptions#with 来将你自定义的 TransitionFactory 应用到加载中。

如果要改变你的 transition 的默认行为,以更好地控制它在不同的加载源(内存缓存,磁盘缓存,或uri)下是否被应用,你可以检查一下你的 TransitionFactory 中传递给 build() 方法的那个 DataSource 。


Glide里的缓存

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

  • 活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片?
  • 内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中?
  • 资源类型(Resource) - 该图片是否之前曾被解码、转换并写入过磁盘缓存?
  • 数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?
    前两步检查图片是否在内存中,如果是则直接返回图片。后两步则检查图片是否在磁盘上,以便快速但异步地返回图片。

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

关于 Glide 缓存的默认大小与它们在磁盘上的位置的更多细节,或如何配置这些参数,请查看 配置 页面。

缓存键(Cache Keys)

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

  • 请求加载的 model(File, Url, Url)
  • 一个可选的 签名(Signature)
    另外,步骤1-3(活动资源,内存缓存,资源磁盘缓存)的缓存键还包含一些其他数据,包括:

  • 宽度和高度

  • 可选的变换(Transformation)
  • 额外添加的任何 选项(Options)
  • 请求的数据类型 (Bitmap, GIF, 或其他)
    活动资源和内存缓存使用的键还和磁盘资源缓存略有不同,以适应内存 选项(Options),比如影响 Bitmap 配置的选项或其他解码时才会用到的参数。

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

配置缓存

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

磁盘缓存策略(Disk Cache Strategy)

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

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

指定 DiskCacheStrategy 非常容易:

GlideApp.with(fragment)  .load(url)  .diskCacheStrategy(DiskCacheStrategy.ALL)  .into(imageView);
  • 仅从缓存加载图片

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

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

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

  • 跳过缓存

如果你想确保一个特定的请求跳过磁盘和/或内存缓存(比如,图片验证码 –译者注),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);

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

缓存的刷新

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

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

定制缓存刷新策略

因为通常改变标识符比较困难或者根本不可能,所以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) {        md.update(ByteBuffer.allocate(Integer.SIZE).putInt(currentVersion).array());    }}

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

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

缓存配置

内存缓存

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

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

@GlideModulepublic 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()));  }}

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

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

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

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

磁盘缓存

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

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

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

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

@GlideModulepublic 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));  }}

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

@GlideModulepublic 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操作而不用触发 严格模式 。

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

配置

默认请求选项

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

@GlideModulepublic 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,你可以使用 RequestManager 的 applyDefaultRequestOptions 方法来设置默认的 RequestOption,并仅作用于一个特定的 Activity 或 Fragment:

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

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

未捕获异常策略 (UncaughtThrowableStrategy)

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

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

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

日志级别

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

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