원문보기:링크

Target 이란

Glide 에서 Target 은 요청과 요청자 사이의 중간자 역할을 수행 합니다. Placeholder 를 표시하고, 리소스를 불러오며, 요청들에 알맞는 크기도 결정해 줍니다. 가장 자주 쓰이는 Target 은 ImageViewTargets 로 ImageView 를 사용해서 placeholder, Drawable 그리고 Bitmap 을 표시할 때 사용 됩니다. 사용자는 베이스 클래스를 상속 받아 하위 클래스를 만들거나 직접 Targets 를 구현할 수도 있습니다.

타켓 지정

into(Target) 함수는 요청을 시작할 뿐만 아니라 어떤 Target 이 요청을 받을지 지정하게 됩니다.

Target<Drawable> target =
  Glide.with(fragment)
    .load(url)
    .into(new Target<Drawable>() {
      ...
    });

Glide 에서는 into(ImageView) 을 위해 요청된 리소스 타입이 적합한 ImageViewImageViewTarget 에 적용될 수 있도록 도와주는 헬퍼(helper) 함수를 제공 합니다:

Target<Drawable> target =
  Glide.with(fragment)
    .load(url)
    .into(imageView);

재사용 및 취소

into(Target)into(ImageView)Target 인스턴스를 리턴 합니다. 만약 이 Target 을 재사용 하신다면 기존에 시작되었던 요청은 취소가 되고 해당 요청의 리소스는 해제 됩니다.:

Target<Drawable> target =
  Glide.with(fragment)
    .load(url)
    .into(new Target<Drawable>() {
      ...
    });
...
// Some time in the future:
Glide.with(fragment)
  .load(newUrl)
  .into(target);

또한 리턴된 Target 을 새로운 로드를 시작하지 않고도 clear() 하고 연관된 리소스들을 해제 할 수 있습니다. :

Target<Drawable> target =
  Glide.with(fragment)
    .load(url)
    .into(new Target<Drawable>() {
      ...
    });
...
// Some time in the future:
Glide.with(fragment).clear(target);

Glide 의 ViewTarget 하위 클래스는 Android Framework 의 getTag()setTag() 함수를 사용해서 각 요청의 View 정보를 저장합니다. 따라서 ViewTarget 하위 클래스를 사용하거나 ImageView 에 로딩할 때 View 를 직접적으로 재사용 하거나 클리어(clear) 할 수 있습니다.:

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

// Some time in the future:
Glide.with(fragment).clear(imageView);

// Or:
Glide.with(fragment)
  .load(newUrl)
  .into(imageView);

추가적으로, ViewTarget 에 한하여,새로운 인스턴스를 각 로딩이나 클리어 할 때 전달 할 수 있습니다.:

Glide.with(fragment)
  .load(url)
  .into(new DrawableImageViewTarget(imageView));

// Some time in the future:
Glide.with(fragment)
  .load(newUrl)
  .into(new DrawableImageViewTarget(imageView));

클리어 (Clear)

当你完成了对资源(BitmapDrawable 等)的使用时,及时清理(clear)你创建的这些 Target 是一个好的实践。即使你认为你的请求已经完成了,也应该使用 clear() 以使 Glide 可以重用被这次加载使用的任何资源 (特别是 Bitmap )。未调用 clear() 会浪费 CPU 和内存,阻塞更重要的加载,甚至如果你在同一个 surface (View, Notification, RPC 等) 上有两个 Target,可能会引发图片显示错误。对于像 SimpleTarget这种无法从一个新实例里跟踪前一个请求的 Target 来说,及时清理尤为重要。

尺寸 (Sizes and dimensions)

默认情况下,Glide 使用目标通过 getSize 方法提供的尺寸来作为请求的目标尺寸。这允许 Glide 选取合适的 URL,下采样,裁剪和变换合适的图片以减少内存占用,并确保加载尽可能快地完成。

View 目标

ViewTarget 通过检查 View 的属性和/或使用一个 OnPreDrawListener 在 View 绘制之前直接测量尺寸来实现 getSize() 方法。因此, Glide 可以自动调整大部分图片以匹配目标 View。加载更小的图片可使 Glide 更快地完成加载 (在缓存到磁盘以后),并使用更少的内存,在图片尺寸一致时还可以增加 Glide 的 BitmapPool 的命中率。

ViewTarget 使用以下逻辑:

  1. 如果 View 的布局参数尺寸 > 0 且 > padding,则使用该布局参数;
  2. 如果 View 尺寸 > 0 且 > padding,使用该实际尺寸;
  3. 如果 View 布局参数为 wrap_content 且至少已发生一次 layout ,则打印一行警告日志,建议使用 Target.SIZE_ORIGINAL 或通过 override() 指定其他固定尺寸,并使用屏幕尺寸为该请求尺寸;
  4. 其他情况下(布局参数为 match_parent0, 或 wrap_content 且没有发生过 layout ),则等待布局完成,然后回溯到步骤1。

有时在使用 RecyclerView时,View 可能被重用且保持了前一个位置的尺寸,但在当前位置会发生改变。为了处理这种场景,你可以创建一个新的 ViewTarget 并为 waitForLayout() 方法传入 true:

@Override
public void onBindViewHolder(VH holder, int position) {
  Glide.with(fragment)
    .load(urls.get(position))
    .into(new DrawableImageViewTarget(holder.imageView, /*waitForLayout=*/ true));
强大的尺寸管理

通常 Glide 在显式地为加载的 View 设置了 dp 尺寸时提供了最快且最可预测的结果。如果无法达到这一点,Glide 也通过 onPreDrawListener 提供了为 layout_weightmatch_parent 和其他相对尺寸的完备鲁棒的支持。最后,如果这些都无法达成,Glide 应该也为 wrap_content 提供了合理的行为。

后备方案

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

定制目标

如果你正在使用一个 Target 且你将要加载的不是可以允许你派生 ViewTarget 的 View, 你讲需要实现 getSize() 方法。

实现 getSize 可能最简单的方案是直接调用回调:

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

使用 Target.SIZE_ORIGINAL 可能非常低效,或如果你的图片足够大可能引发 OOM 。作为替代方案,你也可以为你的 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);
  }
}

如果你的应用内使用一致的图片尺寸,或你确切地知道你需要的尺寸,你也可以传入一个特定尺寸的集合。如果你不知道所需的具体尺寸,但可以异步地得出结果,你也可以使用列表持有在 getSize() 中给出你的任何回调,然后执行你的异步过程并稍后在你得出尺寸之后通知你持有的这些回调。

如果你持有了这些回调,请确保同时实现 removeCallback 以避免内存泄露。

如果需要一个示例,请参考 ViewTarget 中的逻辑。

动画资源和定制目标

如果你只是要加载 GifDrawable,或任何其他资源类型到一个 View,你应该总是尽可能地使用 into(ImageView)。除了优雅的处理或新发起请求之外,Glide的大部分 ViewTarget 实现已经为您处理了 Drawable 动画。如果你确实必须使用定制的 ViewTarget,请确保继承自 ViewTarget 或在新请求开始之前和展示资源结束之后严格地清理从 into(Target) 返回的 Target

如果你并非往 View 中加载图片,而直接使用 ViewTarget 或使用了定制的 Target 比如 SimpleTarget 且你正在加载一个动画资源例如 GifDrawable,你需要确保在 onResourceReady 中调用 start() 来启动这个动画:

Glide.with(fragment)
  .asGif()
  .load(url)
  .into(new SimpleTarget<>() {
    @Override
    public void onResourceReady(GifDrawable resource, Transition<GifDrawable> transition) {
      resource.start();
      // Set the resource wherever you need to use it.
    }
  });

如果你加载的是 BitmapGifDrawable,你可以判断这个可绘制对象是否实现了 Animatable

Glide.with(fragment)
  .load(url)
  .into(new SimpleTarget<>() {
    @Override
    public void onResourceReady(Drawable resource, Transition<GifDrawable> transition) {
      if (resource instanceof Animatable) {
        resource.start();
      }
      // Set the resource wherever you need to use it.
    }
  });