Glide 4.0使用
Glide v4 使用 注解处理器 (Annotation Processor) 来生成出一个 API,在 Application 模块中可使用该流式 API 一次性调用到 RequestBuilder
, RequestOptions
和集成库中所有的选项。
有效使用范围
Generated API 目前仅可以在 Application 模块内使用。这一限制可以让我们仅持有一份 Generated API,而不是各个 Library 和 Application 中均有自己定义出来的 Generated API。这一做法会让 Generated API 的调用更简单,并确保 Application 模块中 Generated API 调用的选项在各处行为一致。
导入
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' //注解依赖包
implementation 'com.github.bumptech.glide:glide:4.7.1' //api包
//Generated API 必须要导入上述注解依赖包才可以使用
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
}
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
对象。
占位符
-
加载占位符(placeholder): 加载图片时展示,加载失败时没有设置error,显示的还是placeholder
-
错误符(error):加载失败时展示,如果加载的url为null,且没有设置fallback时展示error
-
后备回调符(fallback): 在请求的url为
null
时展示,表示允许用户url为null,比如用户头像还未设置时,展示的默认图片
使用
generated api
GlideApp.with(fragment)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.fallback(R.drawable.fallback)
.into(view)
Glide方式
RequestOptions options=new RequestOptions();
options
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.fallback(R.drawable.fallback);
Glide.with(this).load(url).apply(options).into(view);
补充
占位符是异步加载的吗?
No。占位符是在主线程从Android Resources加载的。我们通常希望占位符比较小且容易被系统资源缓存机制缓存起来。
变换是否会被应用到占位符上?
No。Transformation仅被应用于被请求的资源,而不会对任何占位符使用。例如你正在加载圆形图片,你可能希望在你的应用中包含圆形的占位符。但是你也可以考虑自定义一个View来剪裁(clip)你的占位符,达到你的transformation的效果。
在多个不同的View上使用相同的Drawable可行么?
通常可以,但不是绝对的。任何无状态(non-stateful
)的 Drawable(例如 BitmapDrawable
)通常都是ok的。但是有状态的 Drawable 不一样,在同一时间多个 View 上展示它们通常不是很安全,因为多个View会立刻修改(mutate
) Drawable 。对于有状态的 Drawable ,建议传入一个资源ID,或者使用 newDrawable()
来给每个请求传入一个新的拷贝。
View的mutate
如果同一个资源文件被两个drawable
所使用,那么这两个drawable
的状态会保持一致,其中一个drawable
改变透明度,另外一个drawable
也会跟着改变。这时如果想要两个drawable
独立,就需要用到mutate
drawableA.mutate().setAlpha(255);
drawableB.mutate().setAlpha(70);
RequestOptions
Glide4.0之后的原始api中很多样式的设置都需要通过RequestOptions
,包括
- 占位符(
Placeholders
) - 转换(
Transformations
) - 缓存策略(
Caching Strategies
) - 组件特有的设置项,例如编码质量,或
Bitmap
的解码配置等。
比如要实现一个CenterCrop
转换.
RequestOptions cropOptions = new RequestOptions().centerCrop(context);
...
Glide.with(fragment)
.load(url)
.apply(cropOptions)
.into(imageView);
其中apply()可以调用多次,也就是说你可以配置多个RequestOptions
参数,如果 RequestOptions
对象之间存在相互冲突的设置,那么只有最后一个被应用的 RequestOptions
会生效。
另外Glide内部还提供了静态方法来实现CenterCrop
转换
import static com.bumptech.glide.request.RequestOptions.centerCropTransform;
Glide.with(fragment)
.load(url)
.apply(centerCropTransform(context))
.into(imageView);
当然如果你使用的是Generated API
的话,就可以直接使用
GlideApp.with(fragment)
.load(url)
.centerCrop()
.into(imageView);
图片加载过渡动画(TransitionOptions)
使用 TransitionOption
可以应用以下变换:
- View淡入
- 与占位符交叉淡入
- 或者什么都不发生
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
Glide.with(fragment)
.load(url)
.transition(withCrossFade()) //交叉淡入
.into(view);
另外TransitionOptions
是特定资源类型独有的,即加在bitmap
和drawable
的时候需要用到不同的api,如 BitmapTransitionOptions ,而不是 DrawableTransitionOptions。
请求主干RequestOptions
Glide中请求的发起者是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);
}
缩略图请求
Glide.with(fragment)
.load(url)
.thumbnail(Glide.with(fragment)
.load(thumbnailUrl))
.into(imageView);
缩略图应改指向一个低分辨率的图片,这样就快速加在并在主体图片加载之前显示。
如果只有一个远程的url地址,可以强制要求加载一个低分辩率的图像,通过sizeMultiplier
Glide.with(this)
.load("")
.thumbnail(0.25f) // 尺寸是View的百分比
.into(imageView);
在Glide4.3之后还提供了一个失败加载
Glide.with(fragment)
.load(primaryUrl)
.error(Glide.with(fragment)
.load(fallbackUrl))
.into(imageView);
这个error请求之后在正常请求失败之后才会启动.
多重变换
默认情况下,每个 transform()
调用,或任何特定转换方法(fitCenter()
, centerCrop()
, bitmapTransform()
)的调用都会替换掉之前的变换。
其中 MultiTransformation
Glide.with(fragment)
.load(url)
.transform(new MultiTransformation(new FitCenter(), new YourCustomTransformation())
.into(imageView);
自定义变化
可以选择继承自BitmapTransformation
或者DrawableTransitionOptions
,
需要注意的是,对于任何 Transformation
子类,包括 BitmapTransformation
,你都有三个方法你 必须 实现它们,以使得磁盘和内存缓存正确地工作:
equals()
hashCode()
updateDiskCacheKey
ImageView的自动变换
在Glide中,当你为一个 ImageView 开始加载时,Glide可能会自动应用 FitCenter 或 CenterCrop ,这取决于view的 ScaleType
。如果 scaleType
是 CENTER_CROP
, Glide 将会自动应用 CenterCrop
变换。如果 scaleType
为 FIT_CENTER
或 CENTER_INSIDE
,Glide会自动使用 FitCenter
变换。
当然,你总有权利覆写默认的变换,只需要一个带有 Transformation
集合的 RequestOptions 即可。另外,你也可以通过使用 dontTransform()
确保不会自动应用任何变换
Gif的加载动画
Glide可以将 Bitmap
Transformation
应用到 BitmapDrawable
, GifDrawable
, 以及 Bitmap
资源上,因此通常你只需要编写和应用 Bitmap``Transformation
。然而,如果你添加了额外的资源类型,你可能需要考虑派生 RequestOptions
类,并且,在内置的这些 Bitmap
Transformations
之外,你还需要为你的自定义资源类型提供一个 Transformation
。
目标Target
Glide.with(fragment)
.load(url)
.into(imageView); //into() 开始启动请求
通常情况下,我们是这样使用Glide的,其中into(imageView)
这行会将我们期望展示图片的容器ImageView
传递进去。这时大部分人会想Glide
是将下载好的图片直接赋值给ImgaeView
,实际上这中间并不是一步到位的,
ViewTarget<ImageView, Drawable> target = Glide.with(this)
.load("")
.into(imageView);
如上述代码所示into(imageView)
会返回一个ViewTarget
对象,这个ViewTarget
能对加载的图片进行各种操作,另外也能返回最开始的imageView
,通过
ImageView image=target.getView();
既然target
是每个imageView
的包装,所以每个target都是唯一的,如果你使用之前的target
来加载新的图片,就会重置之前的操作,将前一步加载的资源释放掉。
ViewTarget<ImageView, Drawable> target = Glide.with(this)
.load(url)
.into(imageView);
...
// Some time in the future:
Glide.with(fragment)
.load(newUrl)
.into(target); //这时老的imageView上的图片资源会被释放,新图片会替代来图片展示
方法二
Glide.with(this).clear(target);
Glide 的 ViewTarget
子类使用了 Android Framework 的 getTag()
和 setTag()
方法来存储每个请求的相关信息,因此在使用Glide时,你还需要使用到setTag()
时需要注意是否会出现冲突。
最简单的解决方式是调用 setTag(int,object)
加载目标尺寸
默认情况下,Glide 使用目标通过 getSize
方法提供的尺寸来作为请求的目标尺寸。这允许 Glide 选取合适的 URL,下采样,裁剪和变换合适的图片以减少内存占用,并确保加载尽可能快地完成。
target.getSize(new SizeReadyCallback(width,height));
默认情况下Glide的尺寸加载逻辑
- 如果
View
的布局参数尺寸 > 0 且 > padding,则使用该布局参数; - 如果
View
尺寸 > 0 且 > padding,使用该实际尺寸; - 如果
View
布局参数为wrap_content
且至少已发生一次 layout ,则打印一行警告日志,建议使用Target.SIZE_ORIGINAL
或通过override()
指定其他固定尺寸,并使用屏幕尺寸为该请求尺寸; - 其他情况下(布局参数为
match_parent
,0
, 或wrap_content
且没有发生过 layout ),则等待布局完成,然后回溯到步骤1。
Transition(过渡动画)
Transition
指的是从占位符到新加载的图片,或从缩略图到全尺寸图像过渡,而不是从一个请求到另一个请求的动画
另外不同于 Glide v3,Glide v4 将不会默认应用交叉淡入或任何其他的过渡效果。每个请求必须手动应用过渡。
动画的性能提示
Android中的动画代价是比较大的,尤其是同时开始大量动画的时候。 交叉淡入和其他涉及 alpha 变化的动画显得尤其昂贵。 此外,动画通常比图片解码本身还要耗时。在列表和网格中滥用动画可能会让图像的加载显得缓慢而卡顿。为了提升性能,请在使用 Glide 向 ListView , GridView, 或 RecyclerView 加载图片时考虑避免使用动画,尤其是大多数情况下,你希望图片被尽快缓存和加载的时候。作为替代方案,请考虑预加载,这样当用户滑动到具体的 item 的时候,图片已经在内存中了。
在多个请求间交叉淡入
Transitions
并不能让你在不同请求中加载的两个图像之间做过渡。当新的加载被应用到 View 或 Target (查看 Target的文档 )上时,Glide 默认会取消任何已经存在的请求。因此,如果你想加载连个个不同的图片并在它们之间做动画,你无法直接通过 Glide 来完成。等待第一个加载完成并在 View 外持有这个 Bitmap 或 Drawable ,然后开始新的加载并手动在这两者之间做动画,诸如此类的策略看起来有效,但是实际上不安全,并可能导致程序崩溃或图像错误。
相反,最简单的办法是使用包含两个 ImageView
的 ViewSwitcher
来完成。将第一张图片加载到 getNextView()
的返回值里面,然后将第二张图片加载到 getNextView()
的下一个返回值中,并使用一个 RequestListener
在第二张图片加载完成时调用 showNext()
。为了更好地控制,你也可以使用 Android开发者文档 指出的策略。但要记住与 ViewSwitcher
一样,仅在第二次图像加载完成后才开始交叉淡入淡出。
配置
在非application
中不能实现AppGlideModule
内存缓存
默认情况下,Glide使用 LruResourceCache
,这是 MemoryCache
接口的一个缺省实现,使用固定大小的内存和 LRU 算法。LruResourceCache
的大小由 Glide 的 MemorySizeCalculator
类来决定,这个类主要关注设备的内存类型,设备 RAM 大小,以及屏幕分辨率。
应用程序可以自定义 MemoryCache
的大小,具体是在它们的 AppGlideModule
中使用 applyOptions(Context, GlideBuilder)
方法配置 MemorySizeCalculator
:
@GlideModule
public class AppGlideModule 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 AppGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
int memoryCacheSizeBytes = 1024 * 1024 * 20; // 20mb
builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
}
}
Bitmap 池
Glide 使用 LruBitmapPool
作为默认的 BitmapPool
。LruBitmapPool
是一个内存中的固定大小的 BitmapPool
,使用 LRU 算法清理。默认大小基于设备的分辨率和密度,同时也考虑内存类和 isLowRamDevice
的返回值。具体的计算通过 Glide 的 MemorySizeCalculator
来完成,与 Glide 的 MemoryCache
的大小检测方法相似
缓存模式
默认情况下Glide会在开始一个新的图片请求之前按顺序检查下列各级缓存
- 活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片?
- 内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中?
- 资源类型(Resource) - 该图片是否之前曾被解码、转换并写入过磁盘缓存?
- 数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?
前两步检查图片是否在内存中,如果是则直接返回图片。后两步则检查图片是否在磁盘上,以便快速但异步地返回图片。
如果四个步骤都未能找到图片,则Glide会返回到原始资源以取回数据(原始文件,Uri, Url等)。
缓存的图片的key
在 Glide v4 里,所有缓存键都包含至少两个元素:
- 请求加载的 model(File, Url, Url)
- 一个可选的 签名(Signature)
另外,步骤1-3(活动资源,内存缓存,资源磁盘缓存)的缓存键还包含一些其他数据,包括:
- 宽度和高度
- 可选的
变换(Transformation)
- 额外添加的任何
选项(Options)
- 请求的数据类型 (Bitmap, GIF, 或其他)
活动资源和内存缓存使用的键还和磁盘资源缓存略有不同,以适应内存 选项(Options)
,比如影响 Bitmap 配置的选项或其他解码时才会用到的参数。
为了生成磁盘缓存上的缓存键名称,以上的每个元素会被哈希化以创建一个单独的字符串键名,并在随后作为磁盘缓存上的文件名使用。
磁盘缓存策略(Disk Cache Strateg)
默认是AUTOMATIC模式,当你加载远程数据(比如,从URL下载)时,AUTOMATIC
策略仅会存储未被你的加载过程修改过(比如,变换,裁剪–译者注)的原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC
策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。
修改缓存策略
GlideApp.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
其他的一些api
GlideApp.with(this)
.load("")
.onlyRetrieveFromCache(true) //仅从缓存加载 (省流量模式)
.skipMemoryCache(true) //跳过缓存,加载原始图片 (图片验证码)
.diskCacheStrategy(DiskCacheStrategy.NONE) //仅跳过磁盘缓存
.into(imageView);
刷新图片缓存的方式
GlideApp.with(yourFragment)
.load(yourFileDataModel)
.signature(new ObjectKey(yourVersionMetadata))
.into(yourImageView);
加载图片的时候,传入一个可变的signature
数据来控制图片的刷新
清理磁盘
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;
}
}
注意需要开启子线程,而且需要传入application
以防止内存泄露
网友评论