Glide is an Image Loader Library for Android developed by bumptech and is a library that is recommended by Google. It has been used in many Google open source projects including Google I/O 2014 official application. It provides animated GIF support and handles image loading/caching.
Add to your app/build.gradle
file:
dependencies {
implementation 'com.github.bumptech.glide:glide:4.12.0'
// Glide v4 uses this new annotation processor -- see https://bumptech.github.io/glide/doc/generatedapi.html
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
}
Make sure to create a MyAppGlideModule
that simply extends from AppGlideModule
and has the @GlideModule
annotation. For now, the class is empty but later we will show how it can be used to set the default image resolution. If you upgrading from Glide v3, make sure you follow this step too:
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
// new since Glide v4
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {
// leave empty for now
}
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.module.AppGlideModule
// new since Glide v4
@GlideModule
class MyAppGlideModule : AppGlideModule() {
// leave empty for now
}
Make sure to sync your project to Gradle before continuing, since Glide needs to generate the necessary code to invoke GlideApp.with()
in Android Studio.
If you are migrating from Glide v3, make sure to review this guide. Instead of Glide.with()
, you will need to use GlideApp.with()
:
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.into(ivImg)
Resizing images with:
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(300, 200)
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(300, 200)
.into(ivImg)
Placeholder and error images:
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.placeholder(R.drawable.placeholder)
.error(R.drawable.imagenotfound)
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.placeholder(R.drawable.placeholder)
.error(R.drawable.imagenotfound)
.into(ivImg)
Cropping images with:
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.centerCrop()
.into(ivImg);
Modify your MyAppGlideModule
to override applyOptions:
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Glide default Bitmap Format is set to RGB_565 since it
// consumed just 50% memory footprint compared to ARGB_8888.
// Increase memory usage for quality with:
builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888));
}
}
@GlideModule
class MyAppGlideModule : AppGlideModule() {
override fun applyOptions(context: Context, builder: GlideBuilder) {
// Glide default Bitmap Format is set to RGB_565 since it
// consumed just 50% memory footprint compared to ARGB_8888.
// Increase memory usage for quality with:
builder.setDefaultRequestOptions(RequestOptions().format(DecodeFormat.PREFER_ARGB_8888))
}
}
Ideally, an image's dimensions would match exactly those of the ImageView
in which it is being displayed, but as this is often not the case, care must be taken to resize and/or scale the image appropriately. Android's native support for this isn't robust, especially when displaying very large images (such as bitmaps returned from the camera) in smaller image views, which can often lead to errors (see Troubleshooting).
Glide automatically limits the size of the image it holds in memory to the ImageView
dimensions. Picasso has the same ability, but requires a call to fit()
. With Glide, if you don't want the image to be automatically fitted to the ImageView
, you can call override(horizontalSize, verticalSize)
. This will resize the image before displaying it in the ImageView
but without respect to the image's aspect ratio:
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, 200) // resizes the image to 100x200 pixels but does not respect aspect ratio
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, 200) // resizes the image to 100x200 pixels but does not respect aspect ratio
.into(ivImg);
Resizing images in this way without respect to the original aspect ratio will often make the image appear skewed or distorted. In most cases, this should be avoided, and Glide offers two standard scaling transformation options to prevent this: centerCrop
and fitCenter
.
If you only want to resize one dimension, use Target.SIZE_ORIGINAL
as a placeholder for the other dimension:
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, Target.SIZE_ORIGINAL) // resizes width to 100, preserves original height, does not respect aspect ratio
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, Target.SIZE_ORIGINAL) // resizes width to 100, preserves original height, does not respect aspect ratio
.into(ivImg);
centerCrop()
Calling centerCrop()
scales the image so that it fills the requested bounds of the ImageView
and then crops the extra. The ImageView
will be filled completely, but the entire image might not be displayed.
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, 200)
.centerCrop() // scale to fill the ImageView and crop any extra
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, 200)
.centerCrop() // scale to fill the ImageView and crop any extra
.into(ivImg);
fitCenter()
Calling fitCenter()
scales the image so that both dimensions are equal to or less than the requested bounds of the ImageView
. The image will be displayed completely, but might not fill the entire ImageView
.
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, 200)
.fitCenter() // scale to fit entire image within ImageView
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.override(100, 200)
.fitCenter() // scale to fit entire image within ImageView
.into(ivImg);
If an image or set of images aren't loading, make sure to check the Android monitor log in Android Studio. There's a good chance you might see an java.lang.OutOfMemoryError "Failed to allocate a [...] byte allocation with [...] free bytes"
or a Out of memory on a 51121168-byte allocation.
. This is quite common and means that you are loading one or more large images that have not been properly resized.
First, you have to find which image(s) being loaded are likely causing this error. For any given Glide
. call, we can fix this by one or more of the following approaches:
ImageView
by setting layout_width=500dp
in the layout file..override(width, height)
during the Glide load and explicitly set a width or height for the image such as: GlideApp.with(...).load(imageUri).override(500, 500).into(...)
.android:adjustViewBounds="true"
from your ImageView
if present and if you not calling .override()
Applying these tips to all of your Glide image loads should resolve any out of memory issues. As a fallback, you might want to open up your AndroidManifest.xml
and then add android:largeHeap
to your manifest:
<application
android:name=".MyApplication"
...
android:largeHeap="true"
...
Note that this is not generally a good idea, but can be used temporarily to trigger fewer out of memory errors.
If you experience errors loading images, you can create a RequestListener<Drawable>
and pass it in via Glide.listener()
to intercept errors:
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.placeholder(R.drawable.placeholder)
.error(R.drawable.imagenotfound)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
// log exception
Log.e("TAG", "Error loading image", e);
return false; // important to return false so the error placeholder can be placed
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(ivImg);
Glide.with(context)
.load("http://via.placeholder.com/300.png")
.placeholder(R.drawable.placeholder)
.error(R.drawable.imagenotfound)
.listener(object : RequestListener<Drawable?> {
override fun onLoadFailed(
@Nullable e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
// log exception
Log.e("TAG", "Error loading image", e)
return false // important to return false so the error placeholder can be placed
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawabl?>,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
return false
}
})
.into(ivImg)
}
Transformations are supported by an additional third-party library, glide-transformations. First, add the dependencies:
dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
// Glide v4 uses this new annotation processor -- see https://bumptech.github.io/glide/doc/generatedapi.html
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
int radius = 30; // corner radius, higher value = more rounded
int margin = 10; // crop margin, set to 0 for corners with no crop
GlideApp.with(this)
.load("http://via.placeholder.com/300.png")
.centerCrop() // scale image to fill the entire ImageView
.transform(new RoundedCorners(radius, margin))
.into(ivImg);
val radius = 30; // corner radius, higher value = more rounded
val margin = 10; // crop margin, set to 0 for corners with no crop
Glide.with(this)
.load("http://via.placeholder.com/300.png")
.centerCrop() // scale image to fill the entire ImageView
.transform(RoundedCorners(radius, margin))
.into(ivImg)
Circle crop:
Glide.with(this)
.load("http://via.placeholder.com/300.png")
.transform(new CircleCrop())
.into(ivImg);
Glide.with(this)
.load("http://via.placeholder.com/300.png")
.transform(CircleCrop())
.into(ivImg)
Blur:
Glide.with(this)
.load("http://via.placeholder.com/300.png")
.transform(new BlurTransformation())
.into(ivImg);
Glide.with(this)
.load("http://via.placeholder.com/300.png")
.transform(BlurTransformation())
.into(ivImg)
Multiple transforms:
Glide.with(this)
.load("http://via.placeholder.com/300.png")
.transform(new MultiTransformation<Bitmap>(new BlurTransformation(25), new CircleCrop()))
.into(ivImg);
Glide.with(this)
.load("http://via.placeholder.com/300.png")
.transform(MultiTransformation(BlurTransformation(25), CircleCrop()))
.into(ivImg)
By default, Glide uses HttpUrlConnection to handle network requests. Integration libraries can be used to use Volley or OkHttp. See this guide for more information.
There is a way to use Glide to use OkHttp instead, which may be useful if you need to do authenticated requests. First, add the okhttp3-integration
library as a dependency:
dependencies {
implementation 'com.squareup.okhttp3:okhttp:X.X.X'
implementation "com.github.bumptech.glide:okhttp3-integration:4.7.1"
}
By adding the okhttp3-integration
module, an OkHttp instance will be internally created and registered. A OkHttpLibraryGlideModule
will be used to register this component with Glide.
If you already have a shared instance of OkHttpClient, you can define it in your custom MyAppGlideModule
and exclude the use of OkHttpLibraryGlideModule
by using the @Excludes
annotation:
@GlideModule
@Excludes(OkHttpLibraryGlideModule.class) // initialize OkHttp manually
public final class MyAppGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
super.registerComponents(context, glide, registry);
OkHttpClient okHttpClient = new OkHttpClient();
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
}
See this GitHub issue for more context about how to inject an OkHttpClient through Dagger 2.
Review this section if are configuring Glide for use with ProGuard.