Targets
- About
- Specifying Targets
- Cancellation and re-use
- Sizes and dimensions
- Animated Resources and custom Targets.
About
Targets
in Glide act as mediators between requests and requestors. Targets are responsible for displaying placeholders, loaded resources, and determining the appropriate dimensions for each request. The most frequently used Targets are ImageViewTargets
that display placeholders, Drawables, and Bitmaps using ImageView. Users can also implement their own Targets, or subclass any of the available base classes.
Specifying Targets
The into(Target)
method is used not only to start each request, but also to specify the Target that will receive the results of the request:
Target<Drawable> target =
Glide.with(fragment)
.load(url)
.into(new Target<Drawable>() {
...
});
Glide provides a helper method for into(ImageView)
that takes an ImageView
and wraps it in a ImageViewTarget
appropriate for the requested resource type for you:
Target<Drawable> target =
Glide.with(fragment)
.load(url)
.into(imageView);
Cancellation and re-use
Note that both into(Target)
and into(ImageView)
return a Target
instance. If you re-use this Target
to start a new load in the future, any previously started requests will be cancelled and their resources released:
Target<Drawable> target =
Glide.with(fragment)
.load(url)
.into(new Target<Drawable>() {
...
});
...
// Some time in the future:
Glide.with(fragment)
.load(newUrl)
.into(target);
You can also use the returned Target
to clear()
out any pending loads and release any associated resources without starting a new load:
Target<Drawable> target =
Glide.with(fragment)
.load(url)
.into(new Target<Drawable>() {
...
});
...
// Some time in the future:
Glide.with(fragment).clear(target);
Glide’s ViewTarget
subclasses store information about each request in the View
using the Android Framework’s getTag()
and setTag()
methods, so if you’re using a ViewTarget
subclass or loading into an ImageView
, you can re-use or clear the View
directly:
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);
In addition, for ViewTarget
s only, you can pass in a new instance to each load or clear call and allow Glide to retrieve information about previous loads from the View
s tags:
Glide.with(fragment)
.load(url)
.into(new DrawableImageViewTarget(imageView));
// Some time in the future:
Glide.with(fragment)
.load(newUrl)
.into(new DrawableImageViewTarget(imageView));
This will not work unless your Target
extends ViewTarget
or implements setRequest()
and getRequest()
in a way that allows you to retrieve previous requests in new Target instances
.
Clear
It’s always good practice to clear()
out any Target
you create when you’re done with any resource (Bitmap
, Drawable
etc) that you’ve loaded into it. You should use clear()
even if you think your request has finished to let Glide re-use any resources (Bitmaps in particular) used by the load. Failing to clear()
a Target
can waste CPU and memory, block more important loads, or even result in the wrong image appearing if you have two Target
s that display images in the same surface (View, Notification, RPC etc). Clear is especially critical for Target
s like SimpleTarget
that can’t keep track of previous requests from other SimpleTarget
instances.
Sizes and dimensions
By default, Glide uses the size provided by Targets via the getSize()
method as the target size for the request. Doing so allows Glide to choose an appropriate url, downsample, crop, and Transform size appropriate images to minimize memory usage, and make sure loads are as fast as possible.
View Targets
ViewTarget
s implement getSize()
by inspecting the attributes of the View and/or using an OnPreDrawListener
to measure the View immediately before it is rendered. As a result, Glide can automatically resize most images to match the View
they will be displayed in. Loading smaller images lets Glide load them faster (after they’ve been disk cached), use less memory, and increases the hit rate of Glide’s BitmapPool if the View sizes are consistent.
The logic ViewTarget
uses is as follows:
- If the
View
’s layout parameter dimensions are > 0 and > padding, use the layout parameters - If the
View
dimensions are > 0 and > padding, use the view dimensions - If the
View
layout parameters arewrap_content
and at least one layout pass has occurred, log a warning suggesting to useTarget.SIZE_ORIGINAL
or some other fixed size viaoverride()
and use the screen dimensions. - Otherwise (layout parameters are
match_parent
,0
, orwrap_content
and no layout pass has occurred), wait for a layout pass, then go back to step 1.
Sometimes when using RecyclerView
, a View
may be re-used and retain the size from a previous position that will be changed for the current position. To handle those cases, you can create a new [ViewTarget
and pass in true
for waitForLayout
]:
@Override
public void onBindViewHolder(VH holder, int position) {
Glide.with(fragment)
.load(urls.get(position))
.into(new DrawableImageViewTarget(holder.imageView, /*waitForLayout=*/ true));
Performant View Sizes
In general Glide provides the fastest and most predictable results when explicit dp sizes are set on Views it loads into. However when that’s not possible, Glide also provides robust support for layout weights, match_parent
and other relative sizes using OnPreDrawListeners
. Finally, if nothing else will work, Glide should provide reasonable behavior for wrap_content
as well.
Alternatives
If in any case Glide seems to get View sizes wrong, you can always manually override the size, either by extending ViewTarget
and implementing your own logic, or by using the override()
method in RequestOptions
.
Custom Targets
If you’re using a custom Target
and you’re not loading into a View
that would allow you to subclass ViewTarget
, you’ll need to implement the getSize()
method.
The simplest possible way to implement getSize()
is to simply call the provided callback immediately:
@Override
public void getSize(SizeReadyCallback cb) {
cb.onSizeReady(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
}
Using Target.SIZE_ORIGINAL
can be very inefficient or cause OOMs if your image sizes are large enough. As an alternative, You can also pass in a size to your Target
’s constructor and provide those dimensions to the callback:
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);
}
}
Passing in a specific set of dimensions works well when you’re using consistent sizes in your application or when you know exactly what size you need. If you don’t know what size you need but could find out asynchronously, you can retain any callbacks you’re given in getSize()
in a List<SizeReadyCallBack>
, run some asynchronous process and then notify the callbacks you’ve retained when you figure out what the size should be.
Be sure to also implement removeCallback
if you retain callbacks to avoid memory leaks.
For an example see the logic in Glide’s ViewTarget
.
Animated Resources and custom Targets.
If you’re just loading the GifDrawable
, or really any resource type, into a View
you should always use into(ImageView)
when possible. In addition to graceful handling or new requests, most of Glide’s ViewTarget
implementations will already take care of starting animated Drawable
s for you. If you absolutely must have a custom Target
be sure to either or extend ViewTarget
or rigorously clear the Target
returned from into(Target)
before starting new loads or when you’re done displaying the resource you loaded.
If you’re not loading into a View
, using ViewTarget
directly or using a custom Target
like SimpleTarget
and you’re loading an animated resource like GifDrawable
, you need to make sure to start
the animated Drawable
in onResourceReady
:
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.
}
});
If you’re loading either a Bitmap
or a GifDrawable
, you can check to see if the Drawable implements 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.
}
});