原文链接:http://bumptech.github.io/glide/
Github地址:https://github.com/bumptech/glide
上一篇:Android 图片加载(二)图片加载框架Glide 入门篇
一、变换
在Glide中,Transformations 可以获取资源并修改它,然后返回被修改后的资源。通常变换操作是用来完成剪裁或对位图应用过滤器,但它也可以用于转换GIF动画,甚至自定义的资源类型。
Glide 提供了很多内置的变换,包括:
1. 默认变换
我们可以使用多种方式实现变换:
-
直接使用RequestOptions类
RequestOptions requestOptions = new RequestOptions()
.centerCrop();
Glide.with(this)
.load(IMAGE_URL)
.apply(requestOptions) //通过requestOptions设置变换
.into(imageView);
-
RequestOptions类的静态方法
Glide.with(this)
.load(IMAGE_URL)
.apply(RequestOptions.centerInsideTransform()) //静态方法设置变换
.into(imageView);
-
内联方法
Glide.with(this)
.load(IMAGE_URL)
.fitCenter() //内联方法设置变换
.into(imageView);
2. 多重变换
默认情况下,每个 transform()
调用,或任何特定转换方法(fitCenter()
, centerCrop()
, bitmapTransform()
)的调用都会替换掉之前的变换。如果你想在单次加载中应用多个变换,请使用 MultiTransformation
类。
Glide.with(this)
.load(IMAGE_URL)
.transform(new MultiTransformation<>(new CenterInside(), new Rotate(180))) //设置多重变换
// .transform(new CenterCrop(), new Rotate(90)) //效果等同于new MultiTransformation<>()
.into(imageView);
MultiTransformation
构造器变换参数的顺序,决定了这些变换的应用顺序。
3. 自定义变换
尽管 Glide 提供了各种各样的内置 Transformation
实现,如果你需要额外的功能,你也可以实现你自己的Transformation
。
如果你只需要变换 Bitmap
,最好是从继承 BitmapTransformation
开始。BitmapTransformation
为我们处理了一些基础的东西,例如,如果你的变换返回了一个新修改的 Bitmap ,BitmapTransformation
将负责提取和回收原始的 Bitmap。
先看个简单的示例:
public class MyBitmapTransformation extends BitmapTransformation {
private static final String ID = "test.android.com.testapp.bitmap.MyBitmapTransformation";
private static byte[] ID_BYTES = null;
static {
try {
ID_BYTES = ID.getBytes(STRING_CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
if (toTransform.getHeight() == outHeight && toTransform.getWidth() == outWidth) {
return toTransform;
}
return Bitmap.createScaledBitmap(toTransform, outWidth, outHeight, /*filter=*/ true);
}
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(ID_BYTES);
}
@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof MyBitmapTransformation;
}
@Override
public int hashCode() {
return ID.hashCode();
}
}
请特别注意,对于任何Transformation
子类,包括BitmapTransformation
,下面列出的三个方法你都必须实现,以使得磁盘和内存缓存正确地工作:
equals()
hashCode()
updateDiskCacheKey
说明:即使你没有实现这三个方法,也能通过编译,但这可能导致自定义的Transformation无法正常工作。
如果你的 Transformation
没有参数,通常使用一个包含完整包限定名的 static
final
String
来作为一个 ID,它可以构成 hashCode()
的基础,并可用于更新 updateDiskCacheKey()
传入的 MessageDigest
。如果你的Transformation
有参数而且它会影响到Bitmap
被变换的方式,它们也必须被包含到这三个方法中。
例如Glide提供的RoundedCorners
变换,它含有一个圆角弧度参数roundingRadius
,而且roundingRadius
参与到了Bitmap的变换中,因此参数必须包含到三个方法中。RoundedCorners
源码如下所示:
/**
* A {@link BitmapTransformation} which rounds the corners of a bitmap.
*/
public final class RoundedCorners extends BitmapTransformation {
private static final String ID = "com.bumptech.glide.load.resource.bitmap.RoundedCorners";
private static final byte[] ID_BYTES = ID.getBytes(CHARSET);
private final int roundingRadius;
/**
* @param roundingRadius the corner radius (in device-specific pixels).
* @throws IllegalArgumentException if rounding radius is 0 or less.
*/
public RoundedCorners(int roundingRadius) {
Preconditions.checkArgument(roundingRadius > 0, "roundingRadius must be greater than 0.");
this.roundingRadius = roundingRadius;
}
@Override
protected Bitmap transform(
@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
return TransformationUtils.roundedCorners(pool, toTransform, roundingRadius);
}
@Override
public boolean equals(Object o) {
if (o instanceof RoundedCorners) {
RoundedCorners other = (RoundedCorners) o;
return roundingRadius == other.roundingRadius;
}
return false;
}
@Override
public int hashCode() {
return Util.hashCode(ID.hashCode(),
Util.hashCode(roundingRadius));
}
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(ID_BYTES);
byte[] radiusData = ByteBuffer.allocate(4).putInt(roundingRadius).array();
messageDigest.update(radiusData);
}
}
二、自定义模块
- 添加对Glide 的注解解析器的依赖和对OkHttp集成库的依赖
implementation 'com.github.bumptech.glide:glide:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.8.0'
- 创建自定义模块
- 实现AppGlideModule 类
- 给上述实现添加@GlideModule注解。
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
}
}
这样模块类就创建完成了。由于自定义模块类涉及的内容和应用场景比较复杂,可能会抽时间写一篇专门的文章详细介绍它,这里只是为接下来介绍【应用程序选项】做个简单的铺垫。
三、应用程序选项
Glide 允许应用通过模块类 AppGlideModule
实现来完全控制Glide 的内存和磁盘缓存使用。Glide 试图提供对大部分应用程序合理的默认选项,但对于部分应用,可能就需要定制这些值。在你做任何改变时,请注意测量其结果,避免出现性能的倒退。
1. 内存缓存
默认情况下,Glide使用 LruResourceCache
,这是 MemoryCache
接口的一个缺省实现,使用固定大小的内存和 LRU 算法。LruResourceCache
的大小由 Glide 的 MemorySizeCalculator
类来决定,这个类主要关注设备的内存类型,设备 RAM 大小,以及屏幕分辨率。
应用程序可以自定义MemoryCache
的大小,具体是在它们的AppGlideModule
中使用 applyOptions(Context, GlideBuilder)
方法配置MemorySizeCalculator
:
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
//1.根据设备信息计算出合适的内存缓存大小
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
.setMemoryCacheScreens(2)
.build();
builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
//2.也可以直接覆盖缓存大小
int memoryCacheSize = 1024*1024*15; //15M内存缓存
builder.setMemoryCache(new LruResourceCache(memoryCacheSize));
//3.提供自己的MemoryCache实现
builder.setMemoryCache(new MemoryCache() {
//省略方法实现
});
}
}
2. Bitmap池
Glide 使用 LruBitmapPool
作为默认的 BitmapPool
。LruBitmapPool
是一个内存中的固定大小的 BitmapPool
,使用 LRU 算法清理。默认大小基于设备的分辨率和密度,同时也考虑内存类和 isLowRamDevice
的返回值。具体的计算通过 Glide 的MemorySizeCalculator
来完成,与 Glide 的MemoryCache
的大小检测方法相似。
BitmapPool
配置方式与内存缓存相似:
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
//1.根据设备信息计算出合适的BitmapPool缓存大小
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
.setBitmapPoolScreens(4)
.build();
builder.setBitmapPool(new LruBitmapPool(calculator.getBitmapPoolSize()));
//2.也可以直接覆盖BitmapPool大小
int bitmapPoolSizeSize = 1024*1024*15; //15M内存缓存
builder.setBitmapPool(new LruBitmapPool(bitmapPoolSizeSize));
//3.提供自己的BitmapPool实现
builder.setBitmapPool(new BitmapPool() {
//省略方法实现
});
}
}
3. 磁盘缓存
Glide 使用 DiskLruCacheWrapper
作为默认的 磁盘缓存
。DiskLruCacheWrapper
是一个使用 LRU 算法的固定大小的磁盘缓存。默认磁盘大小为 250 MB,位置是在应用的缓存文件夹中的一个 特定目录。
缓存位置可以是外部存储或者内部存储,我们还可以自己设置缓存大小、改变缓存文件夹在外存或内存上的名字:
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
int diskCacheSize = 1024 * 1024 * 100; //100MB 缓存大小
String diskCacheFileName = "diskCacheFileName"; //缓存文件夹名称
//外部存储
builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context));
builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context, diskCacheSize));
builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context,diskCacheFileName, diskCacheSize));
//内部存储
builder.setDiskCache(new InternalCacheDiskCacheFactory(context));
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSize));
builder.setDiskCache(new InternalCacheDiskCacheFactory(context,diskCacheFileName, diskCacheSize));
}
}
应用程序还可以自行选择 DiskCache
接口的实现,并提供自己的 DiskCache.Factory
来创建缓存。Glide 使用一个工厂接口来在后台线程中打开磁盘缓存
,这样方便缓存做诸如检查路径存在性等的IO操作而不用触发 严格模式 。
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
builder.setDiskCache(new DiskCache.Factory() {
@Nullable
@Override
public DiskCache build() {
return new DiskCache() {
//省略方法实现
};
}
});
}
}
三、默认请求选项
通常情况下请求选项
由每个请求来单独指定,但是我们也可以通过 AppGlideModule
配置默认请求选项
以作用于你应用中启动的每个加载:
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
RequestOptions defaultOptions = new RequestOptions()
.centerCrop()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true);
builder.setDefaultRequestOptions(defaultOptions);
}
}
尽管你已经在AppGlideModule
中配置了默认请求选项,任何单独请求里应用的选项还是会覆盖 GlideBuilder 里设置的冲突选项。