大致目录:
//配置
* 设置
* 应用程序
* 程序库 (Libraries)
* 避免在程序库中使用 AppGlideModule
* 应用程序选项
*内存缓存
* Bitmap 池
* 磁盘缓存
* 默认请求选项
* 未捕获异常策略 (UncaughtThrowableStrategy)
* 日志级别
* 注册组件
* 剖析(Anatomy)一个请求
* 排序组件
* prepend()
* append()
* replace()
* 添加一个 ModelLoader
* 模块类和注解
* AppGlideModule
* @GlideModule
* 注解处理器
* 冲突
* 清单解析
//缓存
* Glide里的缓存
* 缓存键(Cache Keys)
* 配置缓存
* 磁盘缓存策略(Disk Cache Strategy)
* 仅从缓存加载图片
* 跳过缓存
* 实现
* 缓存的刷新
* 定制缓存刷新策略
* 资源管理
* 内存缓存
* 永久尺寸调整
* 暂时尺寸调整
* 磁盘缓存
* 永久尺寸修改
* 清理磁盘缓存
一、配置
1.1 设置
为了让 Glide 正常工作,库和应用程序需要做一些固定的步骤。不过,假如你的库不希望注册额外的组件,则这些初始化不是必须的。
1.1.1 应用程序
应用程序 (Applications) 需要:
-
恰当地添加一个
AppGlideModule
实现。 -
(可选)添加一个或多个
LibraryGlideModule
实现。 -
给上述两种实现添加
@GlideModule
注解。 -
添加对 Glide 的注解解析器的依赖。
-
在 proguard 中,添加对
AppGlideModules
的keep
。
在 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
,可按以下步骤执行:
-
添加一个或多个
LibraryGlideModule
实现,以注册新的组件。 -
为每个
LibraryGlideModule
实现,添加@GlideModule
注解。 -
添加 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
作为默认的 BitmapPool
。LruBitmapPool
是一个内存中的固定大小的 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
,你可以使用 RequestManager
的 applyDefaultRequestOptions
方法来设置默认的 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 的功能。可用的组件包括:
-
ModelLoader
用于加载自定义的Model(Url, Uri,任意的 POJO)
和Data(InputStreams, FileDescriptors)
。 -
ResourceDecoder
用于对新的Resources(Drawables, Bitmaps)
或新的 Data 类型(InputStreams, FileDescriptors)
进行解码。 -
Encoder
用于向 Glide 的磁盘缓存写Data (InputStreams, FileDesciptors)
。 -
ResourceTranscoder
用于在不同的资源类型之间做转换,例如,从BitmapResource
转换为DrawableResource
。 -
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 里可以注册很多组件。ModelLoader
和 ResourceDecoder
对于同样的参数类型还可以有多种实现。
1.6 剖析(Anatomy)一个请求
被注册组件的集合(包括默认被 Glide 注册的和在 Module 中被注册的),会被用于定义一个加载路径集合。每个加载路径都是从提供给 load
方法的数据模型到 as
方法指定的资源类型的一个逐步演进的过程。一个加载路径(粗略地)由下列步骤组成:
-
模型
(Model)
-> 数据(Data)
(由模型加载器(ModelLoader)
处理) -
数据
(Data)
-> 资源(Resource)
(由资源解析器(ResourceDecoder)
处理) -
资源
(Resource)
-> 转码后的资源(Transcoded Resource)
(可选;由资源转码器(ResourceTranscoder)
处理)
编码器 (Encoder)
可以在步骤 2 之前往 Glide 的磁盘缓存中写入数据。资源编码器 (ResourceEncoder)
可以在步骤 3 之前往 Glide 的磁盘缓存写入资源。
当一个请求开始后,Glide 将尝试所有从数据模型到请求的资源类型的可用路径。如果任何一个加载路径成功,这个请求就将成功。只有所有可用加载路径都失败时,这个请求才会失败。
1.7 排序组件
在 Registry
类中定义了 prepend()
, append()
和 replace()
方法,它们可以用于设置 Glide 尝试每个 ModelLoader
和 ResourceDecoder
之间的顺序。对组件进行排序允许你注册一些只处理特定树模型的子集的组件(即只处理特定类型的 Uri,或仅特定类型的图像格式),并可以在后面追加一个捕获所有类型的组件以处理其他情况。
-
prepend()
假如你的ModelLoader
或者ResourceDecoder
在某个地方失败了,这时候你想将已有的数据交由 Glide 的默认行为来处理,可以使用prepend()
。prepend()
将确保你的ModelLoader
或ResourceDecoder
先于之前注册的其他组件并被首先执行。如果你的ModelLoader
或者ResourceDecoder
从其handles()
方法中返回了一个false
或失败,所有其他的ModelLoader
或ResourceDecoder
将以它们被注册的顺序执行,一次一个,作为一种回退方案。 -
append()
要处理新的数据类型或提供一个到 Glide 默认行为的回退,使用append()
。append()
将确保你的ModelLoader
或ResourceDecoder
仅在 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 里,所有缓存键都包含至少两个元素:
-
请求加载的
model (File, Url, Url)
-
一个可选的签名
(Signature)
另外,活动资源,内存缓存,资源磁盘缓存的缓存键还包含一些其他数据,包括:
-
宽度和高度
-
可选的变换
(Transformation)
-
额外添加的任何选项
(Options)
-
请求的数据类型
(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;
}
}
网友评论