从v3迁移到v4

原文链接:点击查看

Glide v4 中的一个比较大的改动是Glide库处理选项(centerCrop(), placeholder() 等)的方式。在 v3 版本中,选项由一系列复杂的异构建造者(multityped builders)单独处理。在新版本中,由一个单一类型的唯一一个建造者接管一系列选项对象。Glide 的generated API进一步简化了这个操作:它会合并传入建造者的选项对象和任何已包含的集成库里的选项,以生成一个流畅的 API。

RequestBuilder

对于这类方法:

  1. listener()
  2. thumbnail()
  3. load()
  4. into()

在 Glide v4 版本中,只存在一个 RequestBuilder 对应一个你正在试图加载的类型(Bitmap, Drawable, GifDrawable 等)。 RequestBuilder 可以直接访问对这个加载过程有影响的选项,包括你想加载的数据模型(url, uri等),可能存在的缩略图请求,以及任何的监听器RequestBuilder也是你使用 into() 或者 preload() 方法开始加载的地方:

  1. RequestBuilder<Drawable> requestBuilder = Glide.with(fragment)
  2. .load(url);
  3. requestBuilder
  4. .thumbnail(Glide.with(fragment)
  5. .load(thumbnailUrl))
  6. .listener(requestListener)
  7. .load(url)
  8. .into(imageView);

请求选项

对于这类方法:

  1. centerCrop()
  2. placeholder()
  3. error()
  4. priority()
  5. diskCacheStrategy()

大部分选项被移动到了一个单独的称为 RequestOptions 的对象中,

  1. RequestOptions options = new RequestOptions()
  2. .centerCrop()
  3. .placeholder(R.drawable.placeholder)
  4. .error(R.drawable.error)
  5. .priority(Priority.HIGH);

RequestOptions 允许你一次指定一系列的选项,然后对多个加载重用它们:

  1. RequestOptions myOptions = new RequestOptions()
  2. .fitCenter()
  3. .override(100, 100);
  4. Glide.with(fragment)
  5. .load(url)
  6. .apply(myOptions)
  7. .into(drawableView);
  8. Glide.with(fragment)
  9. .asBitmap()
  10. .apply(myOptions)
  11. .load(url)
  12. .into(bitmapView);

变换

Glide v4 里的 Transformations 现在会替换之前设置的任何变换。在 Glide v4 中,如果你想应用超过一个的 Transformation,你需要使用 transforms() 方法:

  1. Glide.with(fragment)
  2. .load(url)
  3. .apply(new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(20)))
  4. .into(target);

或使用 generated API:

  1. GlideApp.with(fragment)
  2. .load(url)
  3. .transforms(new CenterCrop(), new RoundedCorners(20))
  4. .into(target);

解码格式

在 Glide v3, 默认的 DecodeFormatDecodeFormat.PREFER_RGB_565,它将使用 [Bitmap.Config.RGB_565],除非图片包含或可能包含透明像素。对于给定的图片尺寸,RGB_565 只使用 [Bitmap.Config.ARGB_8888] 一半的内存,但对于特定的图片有明显的画质问题,包括条纹(banding)和着色(tinting)。为了避免RGB_565的画质问题,Glide 现在默认使用 ARGB_8888。结果是,图片质量变高了,但内存使用也增加了。

要将 Glide v4 默认的 DecodeFormat 改回 DecodeFormat.PREFER_RGB_565,请在 AppGlideModule 中应用一个 RequestOption

  1. @GlideModule
  2. public final class YourAppGlideModule extends GlideModule {
  3. @Override
  4. public void applyOptions(Context context, GlideBuilder builder) {
  5. builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565));
  6. }
  7. }

关于使用 AppGlideModules 的更多信息,请查阅 配置 页面。请注意,为了让 Glide 发现你的 AppGlideModule 实现,你必须确保添加了对 Glide 的注解解析器的依赖。关于如何设置这个库的更多信息,请查看 下载和设置

过渡选项

对于这类方法:

  1. crossFade()
  2. animate()

控制从占位符到图片和/或缩略图到全图的交叉淡入和其他类型变换的选项,被移动到了 TransitionOptions 中。

要应用过渡(之前的动画),请使用下列选项中符合你请求的资源类型的一个:

过渡动画通过 RequestBuilder 应用到请求上:

  1. Glide.with(fragment)
  2. .load(url)
  3. .transition(withCrossFade(R.anim.fade_in, 300));

交叉淡入 (Cross fade)

不同于 Glide v3,Glide v4 将不会默认应用交叉淡入或任何其他的过渡效果。每个请求必须手动应用过渡。

要为一个特定的加载应用一个交叉淡入变换效果,你可以使用:

  1. import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
  2. Glide.with(fragment)
  3. .load(url)
  4. .transition(withCrossFade())
  5. .into(imageView);

或:

  1. Glide.with(fragment)
  2. .load(url)
  3. .transition(
  4. new DrawableTransitionOptions
  5. .crossFade())
  6. .into(imageView);

Generated API

为了让使用 Glide v4 更简单轻松,Glide 现在也提供了一套可以为应用定制化生成的 API。应用可以通过包含一个标记了 [AppGlideModule][2 的实现来访问生成的 API。如果你不了解这是怎么工作的,可以查看 Generated API

Generated API添加了一个 GlideApp 类,该类提供了对 RequestBuilderRequestOptions 子类的访问。RequestOptions 的子类包含了所有 RequestOptions 中的方法,以及 GlideExtensions 中定义的方法。RequestBuilder 的子类则提供了生成的 RequestOptions 中所有方法的访问,而不需要你再手动调用 apply 。举个例子:

在没有使用 Generated API 时,请求大概长这样:

  1. Glide.with(fragment)
  2. .load(url)
  3. .apply(centerCropTransform()
  4. .placeholder(R.drawable.placeholder)
  5. .error(R.drawable.error)
  6. .priority(Priority.HIGH))
  7. .into(imageView);

使用 Generated API,RequestOptions 的调用可以被内联:

  1. GlideApp.with(fragment)
  2. .load(url)
  3. .centerCrop()
  4. .placeholder(R.drawable.placeholder)
  5. .error(R.drawable.error)
  6. .priority(Priority.HIGH)
  7. .into(imageView);

你仍然可以使用生成的 RequestOptions 子类来应用相同的选项到多次加载中;但生成的 RequestBuilder 子类可能在多数情况下更为方便。

类型(Type)与目标(Target)

选择资源类型

Glide 允许你指定你想加载的资源类型。如果你指定了一个超类型,Glide 会尝试加载任何可用的子类型。比如,如果你请求的是 Drawable ,Glide 可能会加载一个 BitmapDrawable 或一个 GifDrawable 。而如果你请求的是一个 GifDrawable ,要么会加载出一个 GifDrawable,要么报错–只要图片不是 GIF 的话(即使它凑巧是一个完全有效的图片也是如此)。

默认请求的类型是 Drawable:

  1. Glide.with(fragment).load(url)

如果要明确指定请求 Bitmap:

  1. Glide.with(fragment).asBitmap()

如果要创建一个文件路径(本地图片的最佳选项):

  1. Glide.with(fragment).asFile()

如果要下载一个远程文件到缓存然后创建文件路径:

  1. Glide.with(fragment).downloadOnly()
  2. // or if you have the url already:
  3. Glide.with(fragment).download(url);

Drawables

Glide v3 版本中的 GlideDrawable 类已经被移除,支持标准的Android DrawableGlideBitmapDrawable 也已经被删除,由 BitmapDrawable 代替之。

如果你想知道某个 Drawable 是否是动画(animated),可以检查它是否为 Animatable 的实例。

  1. boolean isAnimated = drawable instanceof Animatable;

Targets

onResourceReady 方法的签名做了一些修改。例如,对于 Drawables:

  1. onResourceReady(GlideDrawable drawable, GlideAnimation<? super GlideDrawable> anim)

现在改为:

  1. onResourceReady(Drawable drawable, Transition<? super Drawable> transition);

类似地, onLoadFailed 的签名也有一些变动:

  1. onLoadFailed(Exception e, Drawable errorDrawable)

改为:

  1. onLoadFailed(Drawable errorDrawable)

如果你想要获得更多导致加载失败的错误信息,你可以使用 RequestListener

取消请求

Glide.clear(Target) 方法被移动到了 RequestManager 中:

  1. Glide.with(fragment).clear(target)

使用 RequestManager 清除之前由它启动的加载过程,通常能提高性能,虽然这并不是强制要求的。Glide v4 会为每一个 Activity 和 Fragment 跟踪请求,所以你需要在合适的层级去清除请求。

配置

在 Glide v3 中,配置使用一个或多个 GlideModule 来完成。而在 Glide v4 中,配置改为使用一个类似但稍微复杂的系统来完成。

关于这个新系统的细节,可以查看配置页面。

应用程序

在早期版本中使用了一个 GlideModule 的应用,可以将它转换为一个 AppGlideModule

在 Glide v3 中,你可能会有一个像这样的 GlideModule

  1. public class GiphyGlideModule implements GlideModule {
  2. @Override
  3. public void applyOptions(Context context, GlideBuilder builder) {
  4. builder.setMemoryCache(new LruResourceCache(10 * 1024 * 1024));
  5. }
  6. @Override
  7. public void registerComponents(Context context, Registry registry) {
  8. registry.append(Api.GifResult.class, InputStream.class, new GiphyModelLoader.Factory());
  9. }
  10. }

在 Glide v4 中,你需要将其转换成一个 AppGlideModule ,它看起来像这样:

  1. @GlideModule
  2. public class GiphyGlideModule extends AppGlideModule {
  3. @Override
  4. public void applyOptions(Context context, GlideBuilder builder) {
  5. builder.setMemoryCache(new LruResourceCache(10 * 1024 * 1024));
  6. }
  7. @Override
  8. public void registerComponents(Context context, Registry registry) {
  9. registry.append(Api.GifResult.class, InputStream.class, new GiphyModelLoader.Factory());
  10. }
  11. }

请注意,@GlideModule 注解不能省略。

如果你的应用拥有多个 GlideModule,你需要把其中一个转换成 AppGlideModule,剩下的转换成 LibraryGlideModule 。除非存在AppGlideModule ,否则程序不会发现 LibraryGlideModule ,因此您不能仅使用 LibraryGlideModule

程序库

拥有一个或多个 GlideModule 的程序库应该使用 LibraryGlideModule 。程序库不应该使用 AppGlideModule ,因为它在一个应用里只能有一个。因此,如果你试图在程序库里使用它,将不仅会妨碍这个库的用户设置自己的选项,还会在多个程序库都这么做时造成冲突。

例如,v3 版本中 Volley 集成库的 GlideModule

  1. public class VolleyGlideModule implements GlideModule {
  2. @Override
  3. public void applyOptions(Context context, GlideBuilder builder) {
  4. // Do nothing.
  5. }
  6. @Override
  7. public void registerComponents(Context context, Registry registry) {
  8. registry.replace(GlideUrl.class, InputStream.class, new VolleyUrlLoader.Factory(context));
  9. }
  10. }

在 v4 版本中可以转换成为一个 LibraryGlideModule

  1. @GlideModule
  2. public class VolleyLibraryGlideModule extends LibraryGlideModule {
  3. @Override
  4. public void registerComponents(Context context, Registry registry) {
  5. registry.replace(GlideUrl.class, InputStream.class, new VolleyUrlLoader.Factory(context));
  6. }
  7. }

清单解析

为了简化迁移过程,尽管清单解析和旧的 GlideModule 接口已被废弃,但它们在 v4 版本中仍被支持。AppGlideModuleLibraryGlideModule,与已废弃的 GlideModule 可以在一个应用中共存。

然而,为了避免检查元数据的性能天花板(以及相关的 bugs ),你可以在迁移完成后禁用掉清单解析,在你的 AppGlideModule 中复写一个方法:

  1. @GlideModule
  2. public class GiphyGlideModule extends AppGlideModule {
  3. @Override
  4. public boolean isManifestParsingEnabled() {
  5. return false;
  6. }
  7. ...
  8. }

using(), ModelLoader, StreamModelLoader.

ModelLoader

ModelLoader API 在 v4 版本中仍然存在,并且它的设计目标仍然和它在 v3 中一样,但有一些细节变化。

第一个细节,ModelLoader 的子类型如 StreamModelLoader ,现在已没有存在的必要,用户可以直接实现 ModelLoader 。例如,一个StreamModelLoader<File> 类现在可以通过 ModelLoader<File, InputStream> 的方式来实现和引用。

第二, ModelLoader 现在并不直接返回 DataFetcher,而是返回 LoadData 。[LoadData] 是一个非常简单的封装,包含一个磁盘缓存键和一个 DataFetcher

第三, ModelLoaders 有一个 handles() 方法,这使你可以为同一个类型参数注册超过一个的 ModelLoader 。

将一个 ModelLoader 从 v3 API转换到 v4 API ,通常是很简单直接的。如果你在你的 v3 ModelLoader 中只是简单滴返回一个 DataFetcher

  1. public final class MyModelLoader implements StreamModelLoader<File> {
  2. @Override
  3. public DataFetcher<InputStream> getResourceFetcher(File model, int width, int height) {
  4. return new MyDataFetcher(model);
  5. }
  6. }

那么你在 v4 替代类上需要做的仅仅只是封装一下这个 data fetcher :

  1. public final class MyModelLoader implements ModelLoader<File, InputStream> {
  2. @Override
  3. public LoadData<InputStream> buildLoadData(File model, int width, int height,
  4. Options options) {
  5. return new LoadData<>(model, new MyDataFetcher(model));
  6. }
  7. @Override
  8. public void handles(File model) {
  9. return true;
  10. }
  11. }

请注意,除了 DataFetcher 之外,模型也被传递给 LoadData 作为缓存键的一部分。这个规则为某些特殊场景提供了更多对磁盘缓存键的控制。大部分实现可以直接将 model 传入 LoadData ,就像上面这样。

如果你仅仅是想为某些 model(而不是所有)使用你的 ModelLoader,你可以在你尝试加载 model 之前使用 handles() 方法来检查它。如果你从 handles 方法中返回了 false ,那么你的 ModelLoader 将不能加载指定的 model ,即使你的 ModelLoader 类型 (在这个例子里是 FileInputStream) 与之匹配。

举个例子,如果你在某个指定文件夹下写入了加密的图片,你可以使用 handles 方法来实现一个 ModelLoader 以从那个特定的文件夹下解密图片,但是并不用于加载其他文件夹下的 File

  1. public final class MyModelLoader implements ModelLoader<File, InputStream> {
  2. private static final String ENCRYPTED_PATH = "/my/encrypted/folder";
  3. @Override
  4. public LoadData<InputStream> buildLoadData(File model, int width, int height,
  5. Options options) {
  6. return new LoadData<>(model, new MyDataFetcher(model));
  7. }
  8. @Override
  9. public void handles(File model) {
  10. return model.getAbsolutePath().startsWith(ENCRYPTED_PATH);
  11. }
  12. }

using()

using API在 Glide v4 中被删除了,这是为了鼓励用户使用 AppGlideModule 一次性地 注册 所有组件,避免对象重用(re-use, 原文如此 –译者注)。你无需每次加载图片时都创建一个新的 ModelLoader ;你应该在 AppGlideModule 中注册一次,然后交给 Glide 在每次加载时检查 model (即你传入 load() 方法的对象)来决定什么时候使用你注册的 `ModelLoader

为了确保你仅为特定的 model 使用你的 ModelLoader ,请像上面展示的那样实现 handles 方法:检查每个 model ,但仅在应当使用你的 ModelLoader 时才返回 true 。

原文:

https://muyangmin.github.io/glide-docs-cn/doc/migrating.html