Você está na página 1de 101

Glide: Customizable Image Loading on

Android
2015 - 2016 Norman Peitek

Also By Norman Peitek


Picasso: Easy Image Loading on Android
Retrofit: Love Working with APIs on Android

Contents
About the Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
What Topics are Covered in this Book? . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Who is this book for? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

i
i
i

Chapter 1 Loading Images, Gifs & Local Videos


Why Use Glide? . . . . . . . . . . . . . . . . .
Adding Glide to Your Setup . . . . . . . . . . .
First Peek: Loading Image from a URL . . . . .
Advanced Loading . . . . . . . . . . . . . . . .
Displaying Gifs & Videos . . . . . . . . . . . .
Chapter Summary . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

1
1
1
2
3
5
7

Chapter 2 Image Display & Placeholders . .


Adapter Usage (ListView, GridView) . . . . .
Sample Gallery Implementation: GridView .
Other Applications: ImageViews as Elements
Placeholders . . . . . . . . . . . . . . . . . .
Fade Animations . . . . . . . . . . . . . . . .
Chapter Summary . . . . . . . . . . . . . . .

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

8
8
12
14
24
25
26

Chapter 3 Image Resizing & Thumbnails .


Image Resizing & Scaling . . . . . . . . .
Thumbnails . . . . . . . . . . . . . . . .
Chapter Summary . . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

28
28
30
32

Chapter 4 Caching & Request Priorities


Caching Basics . . . . . . . . . . . . . .
Request Priorities . . . . . . . . . . . .
Chapter Summary . . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

33
33
36
37

Chapter 5 Callbacks With Glide Targets . . . . . . .


Simple Targets . . . . . . . . . . . . . . . . . . . . .
ViewTarget . . . . . . . . . . . . . . . . . . . . . . .
Loading Images into Notifications and App Widgets
Chapter Summary . . . . . . . . . . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

39
39
42
43
49

.
.
.
.

CONTENTS

Chapter 6 Exceptions and Debugging


Local Debugging . . . . . . . . . . . .
General Exception Logging . . . . . .
Chapter Summary . . . . . . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

50
50
51
52

Chapter 7 Glide Transformations . .


Transformations . . . . . . . . . . .
How to Rotate Your Images . . . . .
Collection of Glide Transformations
Chapter Summary . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

53
53
60
63
64

Chapter 8 Glide Animations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


Animation Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

66
66
68

Chapter 9 Glide Modules . . . . . . . . . . . . . . . . . . . . . . . .


Integrating Network Stacks . . . . . . . . . . . . . . . . . . . . . .
Customize Glide with Modules . . . . . . . . . . . . . . . . . . . .
Glide Module Example: Accepting Self-Signed HTTPS Certificates .
Customize Memory Cache . . . . . . . . . . . . . . . . . . . . . .
Customize Disk Cache . . . . . . . . . . . . . . . . . . . . . . . . .
Custom Cache Implementations . . . . . . . . . . . . . . . . . . .
Request Images in Certain Dimensions . . . . . . . . . . . . . . . .
Dynamic Use of Model Loaders . . . . . . . . . . . . . . . . . . . .
Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

69
69
71
74
81
82
84
84
88
90

Chapter 10 App Release Preparation


Enable ProGuard . . . . . . . . . . .
Configure ProGuard Rules for Glide
Obfuscated Stack Traces . . . . . . .
Chapter Summary . . . . . . . . . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

91
91
92
92
93

Outro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

94

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

About the Book


Due to the popularity of the Glide blog post series published on the Future Studio blog, and the
positive feedback on our Picasso and Retrofit books, weve decided to publish a book on Glide. If
your Android app uses images, this book will save you a ton of time researching and avoid stressful
evenings of bug fixes. If you value your time, this might be something for you.
We cover all topics from the blog post series and additionally add more explanations to each topic
and the example code snippets. Besides a more coherent introduction to Glide, youll also benefit
from more new, book-exclusive advanced topics.
This book is for beginners and advanced readers as well. Well walk you through each topic with
direct reference to code examples. Once youve worked through this book, youll have an extensive
knowledge of image loading on Android with Glide.

What Topics are Covered in this Book?


The list below provides a comprehensive overview of covered topics within the book.

Introduction to Glide
Loading Images, Gifs & Local Videos
Image Display & Placeholders
Image Resizing & Thumbnails
Caching & Request Priorities
Callbacks With Glide Targets
Exceptions and Debugging
Glide Transformations
Glide Animations
Customizing Glide with Glide Modules
App Release Preparations

Who is this book for?


This book is for Android developers who want to get an substantial overview and reference book of
Glide. Youll benefit from the clearly recognizable code examples in regard to your daily work with
Glide.
i

About the Book

ii

Rookie
If youre just starting out with Glide (or coming from any other image loading library like Picasso)
this book will show you all important parts on how to work with images. The provided code snippets
let you jumpstart and create an image-rich app within minutes.

Expert
You already worked with Glide before? Youll profit from our extensive code snippets and can
improve your existing code base. Additionally, the book illustrates various optimizations for an
even better user experience.

Chapter 1 Loading Images, Gifs &


Local Videos
After a lot of success and feedback on our Picasso series, were following the requests to do an
extensive series on another amazing image loading library: Glide. The series will be published in
the last months of 2015 and beginning of 2016. For the first time, we developed the book alongside
with our blog post series. We hope this gives interested users a chance to buy the book before all the
blog posts are published. If you bought this book after the last blog post was published, no worries,
youll still get a ton of additional value with our extra content!
Glide, just like Picasso, can load and display images from many sources, while also taking care of
caching and keeping a low memory impact when doing image manipulations. It has been used by
official Google apps (like the app for Google I/O 2015) and is just as popular as Picasso.

Why Use Glide?


Experienced Android developers can skip this section, but for the starters: you might ask yourself
why you want to use Glide<sup>*</sup> instead of your own implementation.
Android is quite the diva when working with images, since itll load images into the memory pixel
by pixel. A single photo of an average phone camera with the dimensions of 2592x1936 pixels (5
megapixels) will allocate around 19 MB of memory. If you add the complexity of network requests
on spotty wireless connections, caching and image manipulations, you will safe yourself a lot of
time & headache, if you use a well-tested and developed library like Glide.
In this book, well look at many of the features of Glide. Just take a peek at the table of contents and
think if you really want to develop all of these features yourself.
<sup>*</sup> = or any other image loading library, like Picasso, ION, etc.

Adding Glide to Your Setup


Hopefully by now weve convinced you to use a library to handle your image loading requests. If
you want to take a look at Glide, this is the guide for you!
First things first, add Glide to your dependencies. At the time of writing, the last stable version of
Glide is 3.7.0.
https://futurestud.io/blog/picasso-series-round-up/
https://github.com/bumptech/glide
http://developer.android.com/training/displaying-bitmaps/index.html

Chapter 1 Loading Images, Gifs & Local Videos

Gradle
As with most dependencies, pulling it in a Gradle project is a single line in your build.gradle:
1

compile 'com.github.bumptech.glide:glide:3.7.0'

Maven
While we moved all our projects to Gradle, Glide also supports Maven projects:
1
2
3
4
5
6

<dependency>
<groupId>com.github.bumptech.glide</groupId>
<artifactId>glide</artifactId>
<version>3.7.0</version>
<type>aar</type>
</dependency>

First Peek: Loading Image from a URL


Just like Picasso, the Glide library is using a fluent interface. The Glide builder requires at least
three parameters for a fully functional request:
with(Context context) - Context is necessary for many Android API calls. Glide is no
difference here. Glide makes this very convenient by also extracting the context if you pass
an Activity or Fragment object.
load(String imageUrl) - here you specify which image should be loaded. Mostly itll be a
String representing a URL to an Internet image.
into(ImageView targetImageView) - the target ImageView your image is supposed to get
displayed in.
Theoretical explanations are always harder to grasp, so lets look at a hands-on example:

http://en.wikipedia.org/wiki/Fluent_interface
http://developer.android.com/reference/android/content/Context.html

Chapter 1 Loading Images, Gifs & Local Videos

1
2
3
4
5
6
7

ImageView targetImageView = (ImageView) findViewById(R.id.imageView);


String internetUrl = "http://i.imgur.com/DvpvklR.png";
Glide
.with(context)
.load(internetUrl)
.into(targetImageView);

Thats it! If the image at the URL exists and your ImageView is visible, youll see the image in a few
seconds. In case the image does not exist, Glide will return to the error callbacks, which well look
at later. You might already be convinced with this three-line example that Glide is useful to you, but
this is just the tip of the feature iceberg.
In the next section, well start by looking at other options to load images, besides from an Internet
URL. Specifically, well load an image from Android resources, local files and a Uri.

Advanced Loading
In the last section, weve looked at reasons for using Glide and a simple example request to load
an image from an Internet source. But this is not the only possible image source for Glide. Glide
can also load images from the Android resources, files and Uris. In this part, well cover all three
options.

Loading from Resources


First up is loading from Android resources. Instead of giving a String pointing to an Internet URL,
you give a resource int.
1
2
3
4
5
6

int resourceId = R.mipmap.ic_launcher;


Glide
.with(context)
.load(resourceId)
.into(imageViewResource);

If youre confused by the R.mipmap., its Androids new way of handling icons.
Of course, you can set a resource directly by using the methods of the ImageView class. However,
this can be interesting if youre using more advanced topics like dynamic transformations.
http://developer.android.com/reference/android/net/Uri.html
http://android-developers.blogspot.de/2014/10/getting-your-apps-ready-for-nexus-6-and.html

Chapter 1 Loading Images, Gifs & Local Videos

Loading from File


Second up is loading from a file. This can be useful when you let the user select a photo to display
an image (similar to a gallery). The parameter is just a File object. In an example, we look up the
file from our external public directory. Note that this snipped most likely wont work for you (just
if youve Running.jpg on your phone).
1
2
3
4
5
6
7
8
9
10
11
12

// this file probably does not exist on your device.


// However, you can use any file path, which points to an image file
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES),
"Running.jpg"
);
Glide
.with(context)
.load(file)
.into(imageViewFile);

Loading from Uri


Lastly, you can also load images defined by an Uri. The request is no different from the previous
options:
1
2
3
4
5
6
7
8
9

// this could be any Uri. for demonstration purposes


// we're just creating an Uri pointing to a launcher icon
Uri uri = resourceIdToUri(context, R.mipmap.future_studio_launcher);
Glide
.with(context)
.load(uri)
.into(imageViewUri);

The small helper function is a simple conversion from the resourceId to an Uri.

Chapter 1 Loading Images, Gifs & Local Videos

1
2
3
4
5
6
7

public static final String ANDROID_RESOURCE = "android.resource://";


public static final String FOREWARD_SLASH = "/";
private static Uri resourceIdToUri(Context context, int resourceId) {
return Uri.parse(ANDROID_RESOURCE + context.getPackageName() + FOREWARD_SLAS\
H + resourceId);
}

However, the Uri does not have to be generated from a resourceId. It can be any Uri.
The basic loading principles are done, now we can finally look at more interesting stuff. In the next
section, well show you how to display Gifs and local videos with Glide.

Displaying Gifs & Videos


This section will show you a unique feature of Glide: displaying Gifs and local videos. A lot of image
loading libraries ship with the functionality to load and display images. Gif support is something
special and very helpful if you need it in your app. Glide makes the experience with Gifs especially
amazing because its so simple. If you want to display a Gif, you can just use the same regular call
youve been using in the past with images:
1
2
3
4
5
6
7

String gifUrl = "http://i.kinja-img.com/gawker-media/image/upload/s--B7tUiM5l--/\


gf2r69yorbdesguga10i.gif";
Glide
.with( context )
.load( gifUrl )
.into( imageViewGif );

Thats it! This will display the Gif in the ImageView and automatically start playing it. Another great
thing about Glide is that you still can use all your standard calls to manipulate the Gif:
1
2
3
4
5
6

Glide
.with( context )
.load( gifUrl )
.placeholder( R.drawable.cupcake )
.error( R.drawable.full_cake )
.into( imageViewGif );

Chapter 1 Loading Images, Gifs & Local Videos

Gif Check
A potential issue with the code above is, that if the source you provide is not a Gif, and maybe just a
regular image, there is no way of registering that issue. Glide accepts Gifs or images as the .load()
parameter. If you, the developer, expect that the URL is a Gif, which is not the case, Glide cant
automatically detect that. Thus, they introduced an additional method to force Glide into expecting
a Gif .asGif():
1
2
3
4
5
6

Glide
.with( context )
.load( gifUrl )
.asGif()
.error( R.drawable.full_cake )
.into( imageViewGif );

If the gifUrl is a gif, nothing changes. However, unlike before, if the gifUrl is not a Gif, Glide will
understand the load as failed. This has the advantage, that the .error() callback gets called and the
error placeholder gets shown, even if the gifUrl is a perfectly good image (but not a Gif).

Display Gif as Bitmap


If your app displays an unknown list of Internet URLs, it might encounter regular images or Gifs. In
some cases, you might be interested in not displaying the entire Gif. If you only want to display the
first frame of the Gif, you can call .asBitmap() to guarantee the display as a regular image, even if
the URL is pointing to a Gif.
1
2
3
4
5

Glide
.with( context )
.load( gifUrl )
.asBitmap()
.into( imageViewGifAsBitmap );

This should give you all the knowledge to display Gifs with Glide. Its easy, try it!

Display of Local Videos


Another step up from Gifs are videos. Glide is also able to display videos, as long as theyre stored
on the phone. Lets assume you get the file path by letting the user select a video:

Chapter 1 Loading Images, Gifs & Local Videos

1
2
3
4
5
6

String filePath = "/storage/emulated/0/Pictures/example_video.mp4";


Glide
.with( context )
.load( Uri.fromFile( new File( filePath ) ) )
.into( imageViewGifAsBitmap );

One more time: its important to note that this only works for local videos. Videos, which are not
stored on the device (for example Internet URLs), will not work! If you want to display videos from
an Internet URL, look into VideoView.
After reading this section, you should be able to work with Gifs and local videos as well as you
already can with images. Glide makes working with Gifs very smooth and convenient.

Chapter Summary
In this chapter, you learned the basics of Glide. You should have learned:

[x] What Glide is


[x] Why to use Glide
[x] How to integrate Glide into your project
[x] How to load images from various resources
[x] How to load Gifs or local videos

In the next chapter well cover adapter use and Glides caching in ListViews and GridViews. We
also will show you what placeholders Glide offers. Lastly, will demonstrate Glides fade animations.

Additional Chapter Resources


Glide on Github
Glides Javadocs
Android SDK & Android Studio IDE
http://developer.android.com/reference/android/widget/VideoView.html
https://github.com/bumptech/glide
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/package-summary.html
https://developer.android.com/sdk/index.html

Chapter 2 Image Display &


Placeholders
In the first chapter weve shown you the easiest of all cases: loading independent images into a single
ImageView without any image manipulations or changes. In this chapter, well show you a bunch of
options on how to display images. Youll learn in much greater depth about the library architecture
and what Glide does in the background. After this chapter, youll be able to build polished apps with
a very good user experience.

Adapter Usage (ListView, GridView)


The first chapter weve shown you how to load a single image into an ImageView. This section will
demonstrate adapter implementations for ListView and GridView, where each cell contains a single
ImageView. This is similar to many image gallery apps.

Sample Gallery Implementation: ListView


First, well need some test images. We uploaded a selection of the best recipe images from our
eatfoody.com project to imgur:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public static String[] eatFoodyImages = {


"http://i.imgur.com/rFLNqWI.jpg",
"http://i.imgur.com/C9pBVt7.jpg",
"http://i.imgur.com/rT5vXE1.jpg",
"http://i.imgur.com/aIy5R2k.jpg",
"http://i.imgur.com/MoJs9pT.jpg",
"http://i.imgur.com/S963yEM.jpg",
"http://i.imgur.com/rLR2cyc.jpg",
"http://i.imgur.com/SEPdUIx.jpg",
"http://i.imgur.com/aC9OjaM.jpg",
"http://i.imgur.com/76Jfv9b.jpg",
"http://i.imgur.com/fUX7EIB.jpg",
"http://i.imgur.com/syELajx.jpg",
"http://i.imgur.com/COzBnru.jpg",
"http://i.imgur.com/Z3QjilA.jpg",
};
http://eatfoody.com
http://imgur.com/a/uz4uZ

Chapter 2 Image Display & Placeholders

Second, well require an activity, which creates an adapter and sets it for a ListView:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class UsageExampleAdapter extends AppCompatActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_usage_example_adapter);
listView.setAdapter(
new ImageListAdapter(
UsageExampleAdapter.this,
eatFoodyImages
)
);
}
}

Third, lets look at the layout files for the adapter. The layout file for a ListView item is very simple:
1
2
3
4

<?xml version="1.0" encoding="utf-8"?>


<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"/>

This will result in a list of images, which each will have a height of 200dp and match the devices
width. Obviously, this will not result in the prettiest image gallery, but thats not the focus of this
book.
Before we can jump to the result, well need to implement an adapter for the ListView. Well keep
it simple and bind our eatfoody example images to the adapter. Each item will display one image.

http://developer.android.com/reference/android/widget/Adapter.html

Chapter 2 Image Display & Placeholders

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

10

public class ImageListAdapter extends ArrayAdapter {


private Context context;
private LayoutInflater inflater;
private String[] imageUrls;
public ImageListAdapter(Context context, String[] imageUrls) {
super(context, R.layout.listview_item_image, imageUrls);
this.context = context;
this.imageUrls = imageUrls;
inflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = inflater.inflate(
R.layout.listview_item_image,
parent,
false
);
}
Glide
.with(context)
.load(imageUrls[position])
.into((ImageView) convertView);
return convertView;
}
}

The interesting stuff happens in the getView() method of the ImageListAdapter. Youll see that
the Glide call is exactly the same as in the previously used regular loading of images. The way to
utilize Glide stays the same, no matter what application youre trying to cover.
As an advanced Android developer you will know that we need to re-use layouts in ListViews to
create a fast & smooth scrolling experience. One awesomeness of Glide is that it automatically takes
care of the request canceling, clearing of the ImageViews, and loading the correct image into the
appropriate ImageView.

Chapter 2 Image Display & Placeholders

11

Chapter 2 Image Display & Placeholders

12

A Strength of Glide: Caching


When you scroll up and down a lot, youll see that the images are displayed much faster than
previously. On newer phones, there might be no wait times at all. As you can guess, these images
come from cache and are not loaded from the network anymore. Glides cache implementation is
based on the one from Picasso and thus well rounded and will make things a lot easier for you. The
size of the implemented cache depends on the device. In a later chapter, well take a longer look at
the caches and their sizes.
When loading an image, Glide uses three sources: memory, disk and network (ordered from fastest
to slowest). Once again, there is nothing youll have to do. Glide hides all that complexity from you,
while creating intelligently sized caches for you. Again, well take a closer look at the caching in a
later chapter, so dont worry about it for now.

Sample Gallery Implementation: GridView


The implementation for a GridView with image elements is not any different from a ListView
implementation. You actually can use the same adapter. Just switch out the activity layout to a
GridView:
1
2
3
4
5
6
7

<?xml version="1.0" encoding="utf-8"?>


<GridView
android:id="@+id/usage_example_gridview"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="2"/>

This will result in the following design:

Chapter 2 Image Display & Placeholders

13

Chapter 2 Image Display & Placeholders

14

Other Applications: ImageViews as Elements


So far, weve only looked at examples where the entire adapter item is an ImageView. The approach
still applies if one or more ImageViews are only a (small) part of the adapter item. Your getView()
code will look a little different, but the loading of the Glide item would be identical. Lets work
through it!
First, well need the xml layout. For now, lets keep it easy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/listview_item_advanced_imageview"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"/>
<TextView
android:id="@+id/listview_item_advanced_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:paddingLeft="5dp"
android:text="Text"
android:textSize="18sp"
android:textStyle="bold"/>
</LinearLayout>

Now, we just have to adjust the adapter weve used a bit ago. Our new adapter code:

Chapter 2 Image Display & Placeholders

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

15

public class AdvancedImageListAdapter extends ArrayAdapter {


private Context context;
private LayoutInflater inflater;
private String[] imageUrls;
public AdvancedImageListAdapter(Context context, String[] imageUrls) {
super( context, R.layout.listview_item_image, imageUrls );
this.context = context;
this.imageUrls = imageUrls;
inflater = LayoutInflater.from( context );
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = inflater.inflate(
R.layout.listview_item_advanced,
parent,
false
);
}
ImageView iv =
(ImageView) convertView.findViewById( R.id.listview_item_advanced_image\
view );
TextView tv =
(TextView) convertView.findViewById( R.id.listview_item_advanced_tex\
t );
tv.setText( "Position " + position );
Glide
.with( context )
.load( imageUrls[position] )
.asBitmap()
.centerCrop()
.into( iv );

Chapter 2 Image Display & Placeholders

43
44
45

16

return convertView;
}
}

Alright, were done! That was easy. Youll notice an interesting piece in the adapter code: were
calling .centerCrop() to make sure that the ImageView is completely filled and no white space
is left. In case youd want to make sure that the entire image is displayed, you could switch it to
.fitCenter().

Chapter 2 Image Display & Placeholders

17

Chapter 2 Image Display & Placeholders

18

Optimized ListView with Holder Pattern


The experienced Android developers will see that were not really using a holder pattern in our
adapter. Thus, we still can improve the performance of our adapter. We dont need to change
anything for the layout. The adapter is the only place which needs improvement:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

public class AdvancedImageListAdapter extends ArrayAdapter {


private Context context;
private LayoutInflater inflater;
private String[] imageUrls;
public AdvancedImageListAdapter(Context context, String[] imageUrls) {
super( context, R.layout.listview_item_image, imageUrls );
this.context = context;
this.imageUrls = imageUrls;
inflater = LayoutInflater.from( context );
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (null == convertView) {
convertView = inflater.inflate( R.layout.listview_item_advanced, par\
ent, false );
viewHolder = new ViewHolder();
viewHolder.text =
(TextView) convertView.findViewById(R.id.listview_item_advanced_\
text);
viewHolder.icon =
(ImageView) convertView.findViewById(R.id.listview_item_advanced\
_imageview);
convertView.setTag(viewHolder);
} else{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.text.setText( "Position " + position );

Chapter 2 Image Display & Placeholders

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

19

Glide
.with( context )
.load( imageUrls[position] )
.asBitmap()
.centerCrop()
.into( viewHolder.icon );
return convertView;
}
static class ViewHolder {
TextView text;
ImageView icon;
}
}

And our ListView is fast and smooth, even with an image in each row. If youve questions to the
holder pattern, go and read more in the official developer docs.

Rounded ImageView
Lately, there seems to be a push towards rounded images. If you take a look at popular apps right now,
youll see that a lot of images are displayed in a round manner. We could theoretically implement
a transformation for Glide, which cuts the image into a circle and then displays it in a regular
ImageView. In our experience, that doesnt work 100% of the time. Weve moved towards keeping the
Glide call the same and instead use a customized ImageView, which takes over the task of cropping:
1
2
3
4
5
6
7
8
9
10
11
12

public class RoundedImageView extends ImageView {


public RoundedImageView(Context context) {
super( context );
}
public RoundedImageView(Context context, AttributeSet attrs) {
super( context, attrs );
}
public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
http://developer.android.com/training/improving-layouts/smooth-scrolling.html

Chapter 2 Image Display & Placeholders

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

}
public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
Bitmap sbmp;
if (bmp.getWidth() != radius || bmp.getHeight() != radius) {
float smallest = Math.min( bmp.getWidth(), bmp.getHeight() );
float factor = smallest / radius;
sbmp = Bitmap.createScaledBitmap(
bmp,
(int) (bmp.getWidth() / factor),
(int) (bmp.getHeight() / factor),
false
);
} else {
sbmp = bmp;
}
Bitmap output = Bitmap.createBitmap( radius, radius,
Bitmap.Config.ARGB_8888 );
Canvas canvas = new Canvas( output );
final int color = 0xffa19774;
final Paint paint = new Paint();
final Rect rect = new Rect( 0, 0, radius, radius );
paint.setAntiAlias( true );
paint.setFilterBitmap( true );
paint.setDither( true );
canvas.drawARGB( 0, 0, 0, 0 );
paint.setColor( Color.parseColor( "#BAB399" ) );
canvas.drawCircle( radius / 2 + 0.7f,
radius / 2 + 0.7f, radius / 2 + 0.1f, paint );
paint.setXfermode( new PorterDuffXfermode( PorterDuff.Mode.SRC_IN ) );
canvas.drawBitmap( sbmp, rect, rect, paint );
return output;
}
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();

20

Chapter 2 Image Display & Placeholders

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

21

if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy( Bitmap.Config.ARGB_8888, true );
int w = getWidth(), h = getHeight();
Bitmap roundBitmap = getCroppedBitmap( bitmap, w );
canvas.drawBitmap( roundBitmap, 0, 0, null );
}
}

Weve taken the code from stackoverflow and are really satisfied with it.
Lastly, weve to update the ListView item layout to use the RoundedImageView class instead of the
regular ImageView:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<io.futurestud.tutorials.glide.ui.views.RoundedImageView
android:id="@+id/listview_item_advanced_imageview"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"/>
<TextView
android:id="@+id/listview_item_advanced_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
http://stackoverflow.com/questions/26714643/roundedimageview-with-border-color

Chapter 2 Image Display & Placeholders

19
20
21
22
23
24

22

android:paddingLeft="5dp"
android:text="Text"
android:textSize="18sp"
android:textStyle="bold"/>
</LinearLayout>

The adapter can stay the same, since RoundedImageView extends ImageView and we can just keep
the generic version of it. Our app now looks pretty neat:

Chapter 2 Image Display & Placeholders

23

Chapter 2 Image Display & Placeholders

24

At this point, youve learned how to load images with Glide in 90% of the Android use cases. Before
we cover the edge cases, well explain additional capabilities of Glide (besides image loading and
caching). Namely, the next section will be all about placeholders and fade animations.

Placeholders
We probably dont even have to explain or discuss it: empty ImageViews dont look good in any UI.
If youre using Glide, you most likely are loading images via an Internet connection. Depending on
your users environment, this might take a significant amount of time. An expected behavior of an
app is to display a placeholder until the image is loaded and processed.
Glides fluent interface makes this very easy to do! Just call .placeHolder() with a reference to a
drawable (resource) and Glide will display that as a placeholder until your actual image is ready.
1
2
3
4
5

Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.into(imageViewPlaceholder);

For obvious reasons, you cannot set an Internet url as placeholder, since that one would be required
to be loaded as well. App resources and drawables are guaranteed to be available and accessible.
However, as the parameter of the .load() method, Glide accepts all kind of values. These might not
be loadable (no Internet connection, server down, ), deleted or not accessible. In the next section,
well talk about an error placeholder.

Error Placeholder: .error()


Lets assume our app tries to load an image from a website, which is currently down. Glide does
give us the option to get an error callback and take the appropriate action. Well cover that option
later, for now this would be too complicated. In most use cases a placeholder, which signals that the
image could not be loaded is sufficient enough.
The call to Glides fluent interface is identical to the previous example for our pre-display
placeholder, just with a different function call named .error():

Chapter 2 Image Display & Placeholders

1
2
3
4
5
6
7

25

Glide
.with(context)
.load("http://futurestud.io/non_existing_image.png")
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.error(R.mipmap.future_studio_launcher)
// will be displayed if the image cannot be loaded
.into(imageViewError);

Thats it. If the image you define as .load() value cannot be loaded, Glide will display R.mipmap.future_studio_launcher instead. Once again, acceptable parameters for .error() are only already initialized drawable objects or pointers to their resources (like R.drawable.<drawable-keyword>).

.fallback() Placeholder
The .error() placeholder is triggered when the resource is not available, for example the image link
is down. Another type of error is if you pass a null value to Glide. For example, this could happen
if youve a ListView with profile images. Not every profile has an image set and thus youre passing
a null value. If you want to specify a good replacement image when you pass null, use .fallback():
1
2
3
4
5
6
7

String nullString = null; // could be set to null dynamically


Glide
.with(context)
.load( nullString )
.fallback( R.drawable.floorplan )
.into( imageViewNoFade );

The rules are identical to .placeholder(): you can only pass a drawable or a resource id.

Fade Animations
No matter if youre displaying a placeholder before loading the image or not, changing an image of
an ImageView is a pretty significant change in your UI. A simple option to make this change more
smoothly and easier on the eye, is to use a crossfade animation.

Use of .crossFade()
Glide ships with a standard crossfade animation, which is (for the current version 3.7.0) active by
default. If you want to force Glide to show the crossfade animation, all youve to do is a call to
.crossFade() on the builder:

Chapter 2 Image Display & Placeholders

1
2
3
4
5
6
7
8

26

Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.error(R.mipmap.future_studio_launcher)
// will be displayed if the image cannot be loaded
.crossFade()
.into(imageViewFade);

The crossFade() method has another method signature: .crossFade(int duration). If you want to
slow down (or speed up) the animation, feel free to pass a time in milliseconds to the methods. The
default duration of the animation is 300 milliseconds.

Use of .dontAnimate()
If you wish to directly display the image without the small crossfade effect, call .dontAnimate() on
the Glide request builder:
1
2
3
4
5
6
7
8

Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.placeholder(R.mipmap.ic_launcher) // can also be a drawable
.error(R.mipmap.future_studio_launcher)
// will be displayed if the image cannot be loaded
.dontAnimate()
.into(imageViewFade);

This would directly show you the image, without fading it into the ImageView. Please make sure
youve a good reason for doing this though!
It is important to know that all these parameters are independent and can be set without relying on
each other. For example, you could just set .error() without calling .placeholder(). You could set
the crossfade animations without the placeholders. Any combination of the parameters is possible.
Hopefully you understood and learned a lot from this section. It is tremendously important for a
good user experience that the images are not popping in unexpectedly. Also, make it obvious to the
user when something goes wrong. Glide assists you with easy to call functions, which provide the
things you need to have a better app.

Chapter Summary
In this chapter, youve learned a whole set of new usages and capabilities of Glide:

Chapter 2 Image Display & Placeholders

27

[x] How to use Glide in adapter for ListView or GridView


[x] That Glide will automatically cancel and re-load images when the user scrolls
[x] What options Glide has as placeholders
[x] How to utilize and change Glides fade animation

But were not done with learning details about Glide yet. In the next chapter, well look at image
resizing and scaling.

Chapter 3 Image Resizing &


Thumbnails
In the last chapters, youve learned how to load images from various sources and how to use different
kinds of placeholders. The goal for this chapter is to learn what to do when you need to change the
size or scale of images. Also, by the end of the chapter youll know some tools you can use to deal
with very large images.

Image Resizing & Scaling


In one of the last chapters, well look at Glide modules, which are a way to customize Glides
behavior. Well also show you a Glide module, which requests images from the server in a specific
size. For example, if your ImageView is 300x300 pixel, the ideal image would have the identical size.
Unfortunately, you cant always control the size of the source image.
In this section, well look at image resizing, which can help to improve your apps performance when
youve to deal with large images.

Image Resizing with .resize(x, y)


Generally its optimal if your server or API deliver the image in the exact dimensions you need,
which are a perfect trade-off between bandwidth, memory consumption and image quality.
In comparison to Picasso, Glide is much more efficient memory-wise. Glide automatically limits
the size of the image it holds in cache and memory to the ImageView dimensions. Picasso has the
same ability, but requires a call to .fit(). With Glide, if the image should not be automatically
fitted to the ImageView, call override(horizontalSize, verticalSize). This will resize the image
before displaying it in the ImageView.
1
2
3
4
5
6

Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200) // resizes the image to these dimensions (in pixel).
// does not respect aspect ratio
.into(imageViewResize);
http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en

28

Chapter 3 Image Resizing & Thumbnails

29

This option might also be helpful when you load images even though there is no target view with
known dimension yet. For example, if the app wants to warm up the cache in the splash screen,
it cant measure the ImageViews yet. However, if you know how large the images should be, use
.override() to provide a specific size.

Scaling Images
Now, as with any image manipulation, resizing images can really distort the aspect ratio and uglify
the image display. In most of your use cases, you want to prevent this from happening. Glide offers
the general transformations to manipulate the image display. It ships with two standard options
centerCrop and fitCenter.
CenterCrop
CenterCrop() is a cropping technique that 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.


1
2
3
4
5
6
7
8

Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200) // resizes the image to these dimensions (in pixel)
.centerCrop() // this cropping technique scales the image
// so that it fills the requested bounds
// and then crops the extra.
.into(imageViewResizeCenterCrop);

FitCenter
fitCenter() is a cropping technique that 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.
1
2
3
4
5
6

Glide
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.override(600, 200)
.fitCenter()
.into(imageViewResizeFitCenter);

Well look at custom transformations, besides centerCrop() and fitCenter() in a later chapter. For
now, youve learned how to make adjustments to the size and display of images. This should be very
helpful for creating great apps.

Chapter 3 Image Resizing & Thumbnails

30

Thumbnails
So far, weve looked at ways to optimize the user experience by reducing the size of the image.
Unfortunately, that might not always is what you want. If youre implementing a gallery app, you
require that the image is being displayed with the full resolution, but you still would like to avoid the
long waiting time. If the image is very large, the processing can take a significant part of the waiting
time until the image is displayed. In this section, well explore thumbnails as another optimization
option to preserve the original size of the image.

Advantages of Thumbnails
Thumbnails are different than the placeholders from our last chapter. Placeholders have to be
shipped with the app as a bundled resource. Thumbnails are a dynamic placeholder, that can be
loaded from the Internet as well. Thumbnails will be displayed until the actual request is loaded and
processed. If the thumbnail, for whatever reason, arrives after the original image, it does not replace
the original image. It simply will be dismissed.
Hint: another really nice way to smoothen the image loading process is to tint the placeholder
background of the image in the dominant color for the image. Weve written a blog post for
that as well.

Simple Thumbnails
Glide offers two different ways for thumbnails. The first is the simple option where the original
image is used, just in a smaller resolution. This method is particularly useful in combinations of
ListView and detail views. If you already display the image in the ListView, lets just say, in 250x250
pixels, the image will need a much bigger resolution in the detail view. However, from the user
perspective, he already saw a small version of the image, why is there a general placeholder for a
few seconds until the same image gets displayed again (in a higher resolution)?
In this case, it makes a lot more sense to continue to display the 250x250 pixel version in the detail
view and in the background load the full resolution. Glide makes this possible with the .thumbnail()
method. In this case, the parameter is a float as the size multiplier:
1
2
3
4
5

Glide
.with( context )
.load( UsageExampleGifAndVideos.gifUrl )
.thumbnail( 0.1f )
.into( imageView2 );
https://futurestud.io/blog/how-to-get-dominant-color-code-for-picture-with-nodejs/

Chapter 3 Image Resizing & Thumbnails

31

For example, if you pass 0.1f as the parameter, Glide will display the original image reduced to 10%
of the size. If the original image has 1000x1000 pixels, the thumbnail will have 100x100 pixels. Since
the image will be much smaller than the ImageView, you need to make sure the ScaleType of it is
set correctly.
Note that all request settings you applied to the original request are also applied to the thumbnail.
For example, if you used a transformation to make the image gray-scale, the same will happen to
the thumbnail.

Advanced Thumbnails with Complete Different Requests


While the usage of .thumbnail() with a float parameter is easy to set up and can be very effective,
it doesnt always make sense. If the thumbnail needs to load the same full-resolution image over
the network, it might not be faster at all. Thus, Glide provides another option to load and display a
thumbnail.
The second option is to pass a complete new Glide request as the parameter. Lets look at an example:
1
2
3
4
5
6
7
8
9
10
11
12
13

private void loadImageThumbnailRequest() {


// setup Glide request without the into() method
DrawableRequestBuilder<String> thumbnailRequest = Glide
.with( context )
.load( eatFoodyImages[2] );
// pass the request as a a parameter to the thumbnail request
Glide
.with( context )
.load( UsageExampleGifAndVideos.gifUrl )
.thumbnail( thumbnailRequest )
.into( imageView3 );
}

The difference is that the first thumbnail request is completely independent of the second original
request. The thumbnail can be a different resource or image URL, you can apply different
transformations on it, and so on.
Hint, if you want to get crazy, you could make this recursive and apply an additional thumbnail
request to the thumbnail request
This section showed you two different approaches to load thumbnails for images with Glide. Dont
forget this option for your polished apps! It can significantly help to decrease the empty ImageView
times in your app.
http://developer.android.com/reference/android/widget/ImageView.ScaleType.html

Chapter 3 Image Resizing & Thumbnails

32

Chapter Summary
In this chapter, youve learned essential tools for every Android developer, who wants to use Glide.
Its often very important to adjust the size of images, which was one of the important take-aways
of this chapter. You should have learned:

[x] Why and when you need to change the size of images
[x] How to change the size of images
[x] How to avoid distorted image aspect ratios when resizing images
[x] What Glide thumbnails are and how theyre different than placeholders

In the next chapter, well finally go deeper into a topic weve mentioned a few times before: caching!

Chapter 4 Caching & Request


Priorities
In the first three chapters, weve introduced you to a lot of the Glide essentials. No worries, there is
much more to come! In this chapter, well go under the hood and understand the way various Glide
caches work. Additionally, well show you how to prioritize image requests.

Caching Basics
After weve looked at loading, displaying and manipulating images, well move on to optimizing
the process. One of the fundamental features of successful and efficient image loading is caching. In
this section, well go through the basics of caching in Glide.
A well implemented image loading component attempts to minimize the number of network
requests the Android app has to make. Glide is no different here. Glide uses memory and disk caching
by default to avoid unnecessary network requests. Well look at the exact implementation details in
a later chapter. If you cant wait until then, feel free to browse the official document on the topic
or skip ahead for a quick read.
For now, the important take away is that all image requests get cached in memory and on disk.
While the caching is generally something very useful, in some scenarios it might not be the wished
behavior. In the next section, well look at how to change Glides caching behavior for a single
request.

Using Cache Strategies


If you played around with Glide in the past, you noticed that you dont need to do anything extra to
activate caching. It comes right out of the box. However, if you know that an image changes rapidly,
you might want to avoid certain caches.
Glide offers methods to adapt the memory and disk cache behavior. Lets look at the memory cache
first.
Memory Cache
Lets visualize it with a very simple request: loading an image from the Internet to an ImageView:
https://github.com/bumptech/glide/wiki/Caching-and-Cache-Invalidation

33

Chapter 4 Caching & Request Priorities

1
2
3
4
5

34

Glide
.with( context )
.load( eatFoodyImages[0] )
.skipMemoryCache( true )
.into( imageViewInternet );

You already noticed that we called .skipMemoryCache( true ) to specifically tell Glide to skip the
memory cache. This means that Glide will not put the image in the memory cache. Its important to
understand, that this only affects the memory cache! Glide will still utilize the disk cache to avoid
another network request for the next request to the same image URL.
Its also good to know that Glide will put all image resources into the memory cache by default.
Thus, a specific call .skipMemoryCache( false ) is not necessary.
Hint: beware of the fact, that if you make an initial request to the same URL without the
.skipMemoryCache( true ) and then with the method, the resource will get cached in memory.
Make sure youre consistent across all calls to the same resource, when you want to adjust the
caching behavior!
Skipping Disk Cache
As youve learned in the section above, even if you deactivate memory caching, the request image
will still be stored on the devices disk storage. If youve an image, which sits behind the same URL,
but is changing rapidly, you might want to deactivate disk caching as well.
You can change Glides behavior of disk caching with the .diskCacheStrategy() method. Unlike
the .skipMemoryCache() method, it takes an enum with different values instead of a simple boolean.
If you want deactivate the disk cache for a request, use the enum value DiskCacheStrategy.NONE
as the parameter:
1
2
3
4
5

Glide
.with( context )
.load( eatFoodyImages[0] )
.diskCacheStrategy( DiskCacheStrategy.NONE )
.into( imageViewInternet );

The image in this code snippet will not be saved in the disk cache at all. However, by default itll
still use the memory cache! In order to deactivate both caches, combine the method calls:

Chapter 4 Caching & Request Priorities

1
2
3
4
5
6

35

Glide
.with( context )
.load( eatFoodyImages[0] )
.diskCacheStrategy( DiskCacheStrategy.NONE )
.skipMemoryCache( true )
.into( imageViewInternet );

Customize Disk Cache Behavior


As weve mentioned previously, Glide has more than one option for the disk cache behavior. Before
we show you the options, youve to understand that Glides disk caching is fairly complex. For
example, Picasso only caches the full-blown image. Glide, however, caches the original, fullresolution image and additionally smaller versions of that image. For example, if you request an
image with 1000x1000 pixels, and your ImageView is 500x500 pixels, Glide will put both versions of
the image in the cache.
Now youll understand the difference between the enum parameters for the .diskCacheStrategy()
method:
DiskCacheStrategy.NONE caches nothing, as discussed
DiskCacheStrategy.SOURCE caches only the original full-resolution image. In our example
above that would be the 1000x1000 pixel one
DiskCacheStrategy.RESULT caches only the final image, after reducing the resolution (and
possibly transformations)
DiskCacheStrategy.ALL caches all versions of the image (default behavior)
As a last example, if youve an image which you know youll manipulate often and make a bunch
of different versions of it, it makes sense to only cache the original resolution. Thus, wed use
DiskCacheStrategy.SOURCE to tell Glide to only keep the original:
1
2
3
4
5

Glide
.with( context )
.load( eatFoodyImages[2] )
.diskCacheStrategy( DiskCacheStrategy.SOURCE )
.into( imageViewFile );

In this section, youve learned how the basics of Glides image caching work and how you can adjust
the behavior to your needs. In a later chapter, well circle back to this topic to do more advanced
optimizations. Nevertheless, for a start this will give you very effective methods to get the most out
of the caching behavior of Glide.
In the next section, well take a look at another critical piece of a good user experience: prioritizing
image requests!
https://futurestud.io/blog/tag/picasso/

Chapter 4 Caching & Request Priorities

36

Request Priorities
Fairly often, youll run into use cases, where your app will need to load multiple images at the
same time. Lets assume youre building an info screen which has a large hero image at the top and
two smaller, less critical images at the bottom. For the user experience itd be optimal that the hero
image element is getting loaded and displayed first, then afterwards the less urgent ImageViews at
the bottom. Glide supports your wished behavior with the Priority enum and the .priority()
method.
But before we look at the example and the method calls, lets look at the priority enum, which is
used as the parameter for the .priority() method, first.

Getting to Know the Priority Enum


The enum gives you four different options. This is the list ordered with increasing priority:

Priority.LOW
Priority.NORMAL
Priority.HIGH
Priority.IMMEDIATE

Before we jump to the example, you should know that the priorities are not completely strict. Glide
will use them as a guideline and process the requests with the best effort possible, but its not
guaranteed that all images will be loaded in the requested order.
Nevertheless, if youve a use case where certain images are more important, make good use of it!

Usage Example: Hero Element with Child Images


Lets go back to our example from the beginning. Youre implementing an information detail page
with a hero image at the top and smaller images at the bottom. For the best user experience, the hero
image should be loaded first. Thus, wed assign Priority.HIGH to it. Theoretically, that should be
enough. But to make the example a little more interesting, lets also assign the bottom images the
low priority with the .priority(Priority.LOW) call:

Chapter 4 Caching & Request Priorities

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

37

private void loadImageWithHighPriority() {


Glide
.with( context )
.load( UsageExampleListViewAdapter.eatFoodyImages[0] )
.priority( Priority.HIGH )
.into( imageViewHero );
}
private void loadImagesWithLowPriority() {
Glide
.with( context )
.load( UsageExampleListViewAdapter.eatFoodyImages[1] )
.priority( Priority.LOW )
.into( imageViewLowPrioLeft );
Glide
.with( context )
.load( UsageExampleListViewAdapter.eatFoodyImages[2] )
.priority( Priority.LOW )
.into( imageViewLowPrioRight );
}

If you run this example, youll see that in almost all cases the hero images will be displayed first,
despite being much larger image (and consequently requiring more processing time).
Glide gives you very convenient options to prioritize images. Its a fast and easy way to bump up the
user experience a bit. Go through your apps and your code and see if you can apply the techniques
youve just learned!
Request priorities are very helpful, but dont always completely solve the problem. Lets assume
youve to download a very large image, itll take a while to download and process it, no matter what
you set as a priority. In that case you can go back to the previous chapter and study if thumbnails
can help you out.

Chapter Summary
In this chapter, youve learned the basics of Glides caching component and how to utilize request
priorities. You should be able to:
[x] Explain Glides caches and in which order theyre accessed
[x] Know the default settings for the caches
[x] How to request images to be/not to be stored in the memory cache

Chapter 4 Caching & Request Priorities

38

[x] How to request images to be/not to be stored in the disk cache


[x] Change the request priority of requests
In the chapter, well look at a very interesting use case: what if you dont want to load the image
into an ImageView? Instead, maybe you want to load it into notifications, app widgets or custom
views? Well show you everything, just keep reading.

Chapter 5 Callbacks With Glide


Targets
You already have learned a lot about Glide. Youre getting familiar with Glides design and how
they approach certain functions. We hope youre having a certain level of comfort with the library,
because were moving on to a more complex, but very interesting topic: targets.
Whenever you want to request and load an image, which is not going to be displayed in a regular
ImageView, this will be your chapter. Well learn everything to know to load images in non-standard
situations!

Simple Targets
This section of the chapter will be about using callback techniques with Glide. So far, weve always
assumed were loading the images or Gifs into an ImageView. But that might not always be the case.
Next, well look at ways to get the Bitmap of an image resource without specifying an ImageView.

Callbacks in Glide: Targets


So far weve used the convenient Glide builder to load images into an ImageView. Glide hides a ton of
complexity of what happens behind the scenes. Glide does all the network requests and processing
in a background thread and, once the result is ready, changes back to the UI thread and updates the
ImageView.
In this section, we assume that we wont have an ImageView as the destination for the image.
We rather want the Bitmap itself. Glide offers an easy way to access the Bitmap of an image
resource with Targets. Targets are nothing else than callbacks, which return the result after Glides
asynchronous thread is done with all the loading and processing.
Glide offers various kinds of targets while each has an explicit purpose. Well walk through them in
the next few sections. We start with the SimpleTarget.

SimpleTarget
So lets look at a code example:

39

Chapter 5 Callbacks With Glide Targets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

40

private SimpleTarget target = new SimpleTarget<Bitmap>() {


@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
// do something with the bitmap
// for demonstration purposes, let's just set it to an ImageView
imageView1.setImageBitmap( bitmap );
}
};
private void loadImageSimpleTarget() {
Glide
.with( context ) // could be an issue!
.load( eatFoodyImages[0] )
.asBitmap()
.into( target );
}

The first part of the snippet creates a field object, that declares a function, which is called once Glide
has loaded and processed the image. The callback function passes the Bitmap as a parameter. You
can then use the Bitmap object for whatever use you need it.
The second part of the snippet is how we use targets with Glide: exactly the same as with ImageViews!
You can pass either, a Target or an ImageView, as parameter to the .into() method. Glide will do its
magic and return the result to either one. There is one difference, we added the line .asBitmap(),
which forces Glide to return a Bitmap object. Remember, that Glide can also load Gifs or videos. In
order to prevent a clash between the target (which expects a Bitmap) and the unknown resource on
the Internet behind the URL (which could be a Gif), we can call .asBitmap() to tell Glide to only
understand the request as successful, if the resource is an image.

Pay Attention with Targets


Besides knowing how to implement a simple version of Glides Target callback system, youve
learned two additional things.
The first is the field declaration of the SimpleTarget object. Technically, Java/Android would allow
you to declare the target anonymously in the .into() method. However, this significantly increases
the chance that the Android garbage collector removes the anonymous target object before Glide
was done with the image request. Eventually, this would lead to a situation, where the image is
loaded, but the callback can never be called. So please make sure you declare your callback objects
as field objects, so youre protecting it from the evil Android garbage collector ;-)
The second critical part is the Glide builder line .with( context ). The issue here is actually
a feature of Glide: when you pass a context, for example the current app activity, Glide will
automatically stop the request when the requesting activity is stopped. This integration into

Chapter 5 Callbacks With Glide Targets

41

the apps lifecycle is usually very helpful, but can be difficult to work with, if your target is
independent of the apps activity lifecycle. The solution here is to use the application context: .with(
context.getApplicationContext() ). Then Glide will only kill the image request, when the app is
completely stopped itself. Please keep that in mind. Once again, if your request needs to be outside
of the activity lifecycle, use the following snippet:
1
2
3
4
5
6
7

private void loadImageSimpleTargetApplicationContext() {


Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[1]
.asBitmap()
.into( target2 );
}

Target with Specific Size


Another potential issue with targets is that they dont have a specific size. If you pass an ImageView
as the parameter for .into(), Glide will use the size of the ImageView to limit the size of the image.
For example, if the loaded image is 1000x1000 pixels, but the ImageView only 250x250 pixels, Glide
will reduce the image to the smaller size to save processing time and memory. Obviously, this doesnt
work with targets, since there is no known size. However, if you have a specific size in mind, you
can enhance the callback. If you know how large the image should be, you should specify it in your
callback declaration to save some memory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

private SimpleTarget target2 = new SimpleTarget<Bitmap>( 250, 250 ) {


@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
imageView2.setImageBitmap( bitmap );
}
};
private void loadImageSimpleTargetApplicationContext() {
Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[1] )
.asBitmap()
.into( target2 );
}

The only difference to the normal target declaration is the specification of the size in pixels:
new SimpleTarget<Bitmap>( 250, 250 ). This should give you all the knowledge to implement
SimpleTargets in your app.

Chapter 5 Callbacks With Glide Targets

42

ViewTarget
The reason we cant use an ImageView directly can be various. Weve shown you above how to
access a Bitmap. Now, were going one step further. Lets assume youve a Custom View. Glide
doesnt support the loading of images into custom views, since there is no way of knowing where
the image should be set. However, Glide makes it much easier with ViewTargets.
Lets look at our simple custom view, which extends FrameLayout and internally uses an ImageView
and overlays it with a TextView:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

public class FutureStudioView extends FrameLayout {


ImageView iv;
TextView tv;
public void initialize(Context context) {
inflate( context, R.layout.custom_view_futurestudio, this );
iv = (ImageView) findViewById( R.id.custom_view_image );
tv = (TextView) findViewById( R.id.custom_view_text );
}
public FutureStudioView(Context context, AttributeSet attrs) {
super( context, attrs );
initialize( context );
}
public FutureStudioView(
Context context,
AttributeSet attrs,
int defStyleAttr) {
super( context, attrs, defStyleAttr );
initialize( context );
}
public void setImage(Drawable drawable) {
iv = (ImageView) findViewById( R.id.custom_view_image );
iv.setImageDrawable( drawable );
}
}
https://developer.android.com/training/custom-views/index.html

Chapter 5 Callbacks With Glide Targets

43

You cannot use the regular .into() method of Glide here, since our custom view doesnt extend
ImageView. Thus, weve to create a ViewTarget and use that for the .into() method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

private ViewTarget<FutureStudioView, GlideDrawable> viewTarget;


private void loadImageViewTarget() {
FutureStudioView customView =
(FutureStudioView) findViewById( R.id.custom_view );
viewTarget =
new ViewTarget<FutureStudioView, GlideDrawable>( customView ) {
@Override
public void onResourceReady(
GlideDrawable resource,
GlideAnimation<? super GlideDrawable> glideAnimation) {
this.view.setImage( resource.getCurrent() );
}
};
Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[2] )
.into( viewTarget );
}

In the target callback method, we use our created function .setImage(Drawable drawable) in the
custom view class to set the image. Also, make sure that you noticed that weve to pass our custom
view in the constructor of the ViewTarget: new ViewTarget<FutureStudioView, GlideDrawable>(
customView ).
This should cover all your needs with custom views. You could also do additional work in the
callback. For example, we could have analyzed the incoming Bitmap for the dominant color and
set the hex value to the TextView. But were sure youve something in mind already.
In this fairly long section, youve learned the fundamentals of targets in Glide. Youve learned how
to access the Bitmap of an image and how to load images into your own custom views. Next, were
going to continue with examples of targets when we look at how to load images into notifications
and app widgets.

Loading Images into Notifications and App Widgets


In the previous section, weve laid out the basics of loading images into Glide targets. If you havent
read it yet, please review it in order to learn the basics for this section. Even if youre only interested

Chapter 5 Callbacks With Glide Targets

44

in loading images into notifications, itll give you the necessary basics. Now were continuing with
two additional special purpose targets: notifications and app widgets. If you need to load images
into either one of those, keep reading!

Loading Images into Notifications

Notification with Large Icon to Gain Context

Notification icons give important context to the user. Setting the large notification image with the
NotificationCompat.Builder is straight-forward, but the image has to be in form of a Bitmap. If
the image is already available on the phone, thats no problem. However, if the image is not on the
device and needs to be loaded from the Internet, its impossible to use the standard tools.
This is where Glide comes in. Weve already looked at a method how to download the image as a
Bitmap with SimpleTarget. Theoretically, you could utilize that way to load the image into your
notification. But its not necessary, since Glide makes it much more comfortable for you by providing
a convenient NotificationTarget.
NotificationTarget
So, lets look at some code. You know by now how Glide targets work, thus we wont go over it
again. In order to display a large image in the notification, you could use RemoteViews and display
http://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html

45

Chapter 5 Callbacks With Glide Targets

a custom notification.

Our Final Result

Our layout for the custom notification is simple:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="2dp">
<ImageView
android:id="@+id/remoteview_notification_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="2dp"
android:layout_weight="0"
android:scaleType="centerCrop"/>

Chapter 5 Callbacks With Glide Targets

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/remoteview_notification_headline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textSize="12sp"/>
<TextView
android:id="@+id/remoteview_notification_short_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingBottom="2dp"
android:singleLine="true"
android:textSize="14sp"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

The following code creates a custom notification with the layout above for us.
1
2
3
4
5
6
7
8
9
10

final RemoteViews rv = new RemoteViews(


context.getPackageName(),
R.layout.remoteview_notification
);
rv.setImageViewResource(
R.id.remoteview_notification_icon,
R.mipmap.future_studio_launcher
);

46

Chapter 5 Callbacks With Glide Targets

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

47

rv.setTextViewText(R.id.remoteview_notification_headline, "Headline");
rv.setTextViewText(R.id.remoteview_notification_short_message, "Short Message");
// build notification
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.future_studio_launcher)
.setContentTitle("Content Title")
.setContentText("Content Text")
.setContent(rv)
.setPriority( NotificationCompat.PRIORITY_MIN);
final Notification notification = mBuilder.build();
// set big content view for newer androids
if (android.os.Build.VERSION.SDK_INT >= 16) {
notification.bigContentView = rv;
}
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(NOTIFICATION_ID, notification);

This snipped creates three important objects for us, the notification and the RemoteViews and our
constant NOTIFICATION_ID. Well need those to create the notification target:
1
2
3
4
5
6
7
8
9
10

private NotificationTarget notificationTarget;


...
notificationTarget = new NotificationTarget(
context,
rv,
R.id.remoteview_notification_icon,
notification,
NOTIFICATION_ID);

Lastly, we need to call Glide itself and as weve done in the previous posts, set the target as the
.into() parameter:

Chapter 5 Callbacks With Glide Targets

1
2
3
4
5

48

Glide
.with( context.getApplicationContext() ) // safer!
.load( eatFoodyImages[3] )
.asBitmap()
.into( notificationTarget );

The result is that as soon as Glide has the image loaded, itll get displayed in our custom notification.
Awesome :)

App Widgets
Lets move on to another Glide target. App widgets are a part of Android since quite a while.
If your app provides app widgets and contains images, this should be interesting to you. The
AppWidgetTarget of Glide can help significantly to make this very straight-forward.
Lets look at a simple sample AppWidgetProvider:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public class FSAppWidgetProvider extends AppWidgetProvider {


private AppWidgetTarget appWidgetTarget;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews rv = new RemoteViews(
context.getPackageName(),
R.layout.custom_view_futurestudio
);
appWidgetTarget = new AppWidgetTarget(
context,
rv,
R.id.custom_view_image,
appWidgetIds
);
Glide
.with( context.getApplicationContext() ) // safer!
.load( GlideExampleActivity.eatFoodyImages[3] )
.asBitmap()
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/request/target/AppWidgetTarget.html

Chapter 5 Callbacks With Glide Targets

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

49

.into( appWidgetTarget );
pushWidgetUpdate(context, rv);
}
public static void pushWidgetUpdate(Context context, RemoteViews rv) {
ComponentName myWidget = new ComponentName(
context,
FSAppWidgetProvider.class
);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, rv);
}
}

The important lines are the declaration of the appWidgetTarget object and the Glide builder.
Good news here: you dont need to customize AppWidgetTarget any further with overriding the
onResourceReady method. Glide does everything automatically for you. Very nice!

Chapter Summary
Its time to conclude our adventure into Glide targets. Youve learned how to load images
asynchronously for all kind of purposes, ImageViews, notifications, Bitmap callbacks etc. You should
be able to:

[x] Explain what Glide targets are


[x] How to use SimpleTarget to access the bitmap of an image
[x] Use ViewTarget to load images into custom views
[x] Use NotificationTarget to load images into notifications
[x] Use AppWidgetTarget to load images into app widgets

Next, well look how to deal with errors. What happens when something went wrong? What happens
when the URL doesnt exist or isnt valid? Stay tuned.

Chapter 6 Exceptions and


Debugging
After introducing all fundamental concepts of Glide, well move to a developer topic. In this chapter,
well show you a few helpful ways to debug issues you might experience during the image loading
process with Glide.

Local Debugging
Glides GeneralRequest class offers a method to set the log level. Unfortunately, you dont have
easy access to the class in production use. Nevertheless, there is a very simple way to get Glides
debug logs. All youve to do is to activate it via the adb shell. Open your terminal and use the
following command:
1

adb shell setprop log.tag.GenericRequest DEBUG

The last part DEBUG comes from the standard Android log constants. Thus, your options with
increasing priority as the parameter are:

VERBOSE
DEBUG
INFO
WARN
ERROR

The output, in case of a non-existing image, would look like this:


1
2
3
4

io.futurestud.tutorials.glide D/GenericRequest: load failed


io.futurestud.tutorials.glide D/GenericRequest: java.io.IOException: Request fai\
led 404: Not Found
...

As you already guessed, this only works when you have physical access to the device and while
youre developing and testing your app. For logging in production app, youll need a different way.
The answer is once again callbacks, which well explore in the next section.
http://bumptech.github.io/glide/javadocs/340/com/bumptech/glide/request/GenericRequest.html
http://developer.android.com/reference/android/util/Log.html

50

Chapter 6 Exceptions and Debugging

51

General Exception Logging


Glide doesnt offer direct access to the GenericRequest class to set the logging, but you can catch the
exception in case something goes wrong with the request. For example, if an image is not available,
Glide would (silently) throw an exception and show the drawable youve specified in .error(). If
you explicitly want to know the exception, create a listener and pass it to the .listener() method
on the Glide builder.
First, create the listener as a field object to avoid that it gets garbage collected:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

private RequestListener<String, GlideDrawable> requestListener =


new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(
Exception e,
String model,
Target<GlideDrawable> target,
boolean isFirstResource) {
// todo log exception
// important to return false so the error
// placeholder can be placed
return false;
}
@Override
public boolean onResourceReady(
GlideDrawable resource,
String model,
Target<GlideDrawable> target,
boolean isFromMemoryCache,
boolean isFirstResource) {
return false;
}
};

In the onException() method, you can catch the problem and decide what you need to do, for
example log it. Its important that you return false in the onException() method, if Glide should
handle the consequences, like displaying an error placeholder itself.
You can set the listener weve just created on the Glide builder:

Chapter 6 Exceptions and Debugging

1
2
3
4
5
6

52

Glide
.with( context )
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.listener( requestListener )
.error( R.drawable.cupcake )
.into( imageViewPlaceholder );

The .error() placeholder is not required to make the logging work. However, the R.drawable.cupcake
gets only displayed, if you return false in the onException() method of the listener.

Chapter Summary
In this chapter, youve learned some ways to debug and log exceptions that might occur when
working with Glide. Choose one of the ways, depending on your need. Lets review your learnings:
[x] What options youve to debug Glide requests
[x] How to activate logging on local devices
[x] How to use listeners for all app installations
Starting with the next chapter, well move towards more advanced topics. Our topic in the following
chapter is custom image transformations!

Chapter 7 Glide Transformations


In the previous chapters, youve learned all the fundamentals required to utilize the standard
capabilities of Glide. Starting with this chapter, and moving forward, well deep dive into a bunch
of advanced topics. Next, well take a closer look at transformations.

Transformations
Transformations can be used as an image manipulation before the image is being displayed. For
example, if your app needs to display an image in grey-scale, but only has access to the original
fully-colored version, you could use a transformation to manipulate the bitmap from a happy colored
version to a dismal grey version. Dont understand us wrong, transformations are not limited to
colors. You can change pretty much anything of an image: size, bounds, colors, pixel positioning,
and more! Glide already ships with two transformations, and weve looked at them earlier in image
resizing: fitCenter and centerCrop. Both options have such a significance, that they earned their
own Glide method, so we wont include them in this part of the book.

Implementing Your Own Transformation


In order to apply your own custom transformations, youll need to create a new class, which
implements the Transformation interface. The methods you are required to implement are
pretty complex and youll have to have quite the insight of the inner architecture of Glide to
make it work well. If you just want to transform regular bitmaps for images (no Gifs/videos!),
we would recommend to work with the abstract BitmapTransformation class. It simplifies the
implementation quite a bit and should cover 95% of the use cases.
So, lets take a look at a sample BitmapTransformation implementation. If you read our blog
regularly, you know our favorite transformation is blurring images with Renderscript. We can
apply almost the same code weve used back then to make it a Glide transformation. Since we have
to extend the BitmapTransformation class, we already have our framework:

http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/Transformation.html
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/resource/bitmap/BitmapTransformation.html
https://futurestud.io/blog/how-to-blur-images-efficiently-with-androids-renderscript/

53

Chapter 7 Glide Transformations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class BlurTransformation extends BitmapTransformation {


public BlurTransformation(Context context) {
super( context );
}
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight) {
return null; // todo
}
@Override
public String getId() {
return null; // todo
}
}

Now we fill in our code from the previous blog post to blur an image with Renderscript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public class BlurTransformation extends BitmapTransformation {


private RenderScript rs;
public BlurTransformation(Context context) {
super( context );
rs = RenderScript.create( context );
}
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight)
{
Bitmap blurredBitmap = toTransform.copy( Bitmap.Config.ARGB_8888, true );

54

Chapter 7 Glide Transformations

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55

// Allocate memory for Renderscript to work with


Allocation input = Allocation.createFromBitmap(
rs,
blurredBitmap,
Allocation.MipmapControl.MIPMAP_FULL,
Allocation.USAGE_SHARED
);
Allocation output = Allocation.createTyped(rs, input.getType());
// Load up an instance of the specific script that we want to use.
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(
rs,
Element.U8_4(rs)
);
script.setInput(input);
// Set the blur radius
script.setRadius(10);
// Start the ScriptIntrinisicBlur
script.forEach(output);
// Copy the output to the blurred bitmap
output.copyTo(blurredBitmap);
toTransform.recycle();
return blurredBitmap;
}
@Override
public String getId() {
return "blur";
}
}

Once again, if youre confused by the code block in transform(), feel free to read up our previous
post. The getId() method describes an unique identifier for this particular transformation. Glide
uses that key as part of the caching system. Makes sure you make it unique to avoid unexpected
issues.
In the next section, well learn how to apply the transformation weve just created.
https://futurestud.io/blog/how-to-blur-images-efficiently-with-androids-renderscript/

Chapter 7 Glide Transformations

56

Apply a Single Transformation


Glide has two ways of applying a transformation. The first is to pass an instance of your class as a
parameter to .transform(). You can apply any transformation there, no matter if its for an image or
Gif. The other option is to use .bitmapTransform(), which only accepts transformations for bitmaps.
Since our implementation above is designed for bitmaps, we could use either one:
1
2
3
4
5
6
7

Glide
.with( context )
.load( eatFoodyImages[0] )
.transform( new BlurTransformation( context ) )
// this would work too:
//.bitmapTransform( new BlurTransformation( context ) )
.into( imageView1 );

This is enough to get Glide to automatically apply our blurring algorithm to the image weve loaded
from the Internet. Very helpful!

Chapter 7 Glide Transformations

57

Chapter 7 Glide Transformations

58

Apply Multiple Transformations


Usually, Glides fluent interface allows methods to be chained. However, with transformations this
is not the case. Make sure you only call .transform() or .bitmapTransform() once, or the previous
configuration will be overwritten! Nevertheless, you can still apply multiple transformations by passing multiple transformation objects as the parameter into .transform() (or .bitmapTransform()):
1
2
3
4
5
6
7
8

Glide
.with( context )
.load( eatFoodyImages[1] )
.transform(
new GreyscaleTransformation( context ),
new BlurTransformation( context )
)
.into( imageView2 );

In this code snippet, we apply a grey-scale to the image, and afterwards blur it. Glide executes both
transformations automatically for you. Awesome!

Chapter 7 Glide Transformations

59

Chapter 7 Glide Transformations

60

Hint: You cannot use .centerCrop() or .fitCenter() when you use transformations. Otherwise
your transformation will not be applied!

How to Rotate Your Images


A little while ago, we got a question on how to rotate images with Glide, since Picasso offers this
functionality out-of-the-box. Unfortunately, Glide does not offer this is one little method call, but
in this section well show you how to make it almost as easy. Of course, since this is located in the
Glide transformations chapter, well utilize a transformation.
Actually, the android.graphics.Matrix class offers exactly what we need (and a lot more). The code
snippet to rotate an image is really straight-forward:
1
2
3
4
5
6
7

Bitmap toTransform = ... // wherever your bitmap is coming from


Matrix matrix = new Matrix();
matrix.postRotate(rotateRotationAngle);
Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHe\
ight(), matrix, true);

However, in order to make it much more useful to us, especially in the context of using Glide, well
wrap this in a BitmapTransformation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class RotateTransformation extends BitmapTransformation {


private float rotateRotationAngle = 0f;
public RotateTransformation(Context context, float rotateRotationAngle) {
super( context );
this.rotateRotationAngle = rotateRotationAngle;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth\
, int outHeight) {
Matrix matrix = new Matrix();
https://futurestud.io/blog/picasso-image-rotation-and-transformation
http://developer.android.com/reference/android/graphics/Matrix.html

Chapter 7 Glide Transformations

16
17
18
19
20
21
22
23
24
25
26

61

matrix.postRotate(rotateRotationAngle);
return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), to\
Transform.getHeight(), matrix, true);
}
@Override
public String getId() {
return "rotate" + rotateRotationAngle;
}
}

Since youre already familiar with transformation, this section should not raise any questions. So
lets look at a few examples of our new transformation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14

private void loadImageOriginal() {


Glide
.with( context )
.load( eatFoodyImages[0] )
.into( imageView1 );
}
private void loadImageRotated() {
Glide
.with( context )
.load( eatFoodyImages[0] )
.transform( new RotateTransformation( context, 90f ))
.into( imageView3 );
}

Chapter 7 Glide Transformations

62

Chapter 7 Glide Transformations

63

Of course, you can change the second parameter on how many degrees the image is going to be
rotated to anything you need. You could even set it dynamically! This should provide all the code
and knowledge you need to rotate images in Glide, even if its not directly provided by the library.

Collection of Glide Transformations


If you already have an idea what kind of transformation you might be able to use in your app, take
a second to look at the following library: glide-transformations. It provides a whole collection of
various glide transformations. Its worth to check if your idea might already be implemented.
This extra transformation library ships with two different versions. The extended version includes
more transformations, which are computed on the devices GPU. They require an additional
dependency, so the setup for the two versions is a little bit different. You should look through the
list of transformations and decide which version you need.

Setup for Glide Transformations


The setup is easy! For the basic version you can just add another line to your current build.gradle:
1
2
3

dependencies {
compile 'jp.wasabeef:glide-transformations:2.0.0'
}

If you would like to use the GPU transformations:


1
2
3
4
5
6
7
8
9

repositories {
jcenter()
mavenCentral()
}
dependencies {
compile 'jp.wasabeef:glide-transformations:2.0.0'
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0'
}

If you want to use the BlurTransformation, you need to do one more step. In case you havent
already done it, add the following snippet to your build.gradle:

https://github.com/wasabeef/glide-transformations

Chapter 7 Glide Transformations

1
2
3
4
5
6
7
8

64

android {
...
defaultConfig {
...
renderscriptTargetApi 19
renderscriptSupportModeEnabled true
}
}

If you want to know about this last step, take a look at our blog post about Renderscript.

Usage of Glide Transformations


After youve synced Android Studio with the build.gradle file, you can go ahead and use the
transformation collection. The usage pattern is the same as with your own, custom transformations.
Lets assume we would like to blur an image with the collections blur transformation:
1
2
3
4
5
6
7
8
9
10
11

Glide
.with( context )
.load( eatFoodyImages[2] )
.bitmapTransform(
new jp.wasabeef.glide.transformations.BlurTransformation(
context,
25,
2
)
)
.into( imageView3 );

You can also apply a list of transformations just like weve seen above. The .bitmapTransform()
method accepts both, a single transformation or a list of transformations!

Chapter Summary
In this chapter, youve learned a very useful tool of Glide: transformations. Youve learned how to
implement and apply pre-defined and custom transformations. We hope this gives you everything
you need to implement it in your app! In order to make sure you took all the learnings with you,
please review them:
https://futurestud.io/blog/how-to-use-the-renderscript-support-library-with-gradle-based-android-projects/

Chapter 7 Glide Transformations

65

[x] What are Glide transformations?


[x] How to implement your own transformation
[x] How to apply one or more of your own transformations
[x] How to integrate the transformation library
[x] How to apply one or more transformations from the library

After looking at a very customizable feature in this chapter, well continue to do so in the next one.
Next, well look at custom animations.

Chapter 8 Glide Animations


In the last chapter, weve looked at transforming images before displaying them. We continue in this
chapter with the option of animating the display of images.

Animation Basics
Its pretty important to make a smooth transition from image to image. Users appreciate no large
unexpected changes popping up in their apps. Thats what Glide animations are for. Glide ships with
a standard animation to soften the change in your UI. Weve looked at .crossFade() in an earlier
chapter.
But in this chapter, we want to look at other options than the .crossFade(). Glide offers two* options
to set an animation. Both versions work with the .animate() method, but pass different parameters.
Before we look at code, let us point out that the animations are only used when the image could not
be served from a cache. If the image was in a cache, it should be served very quickly and thus the
animation isnt necessary and isnt displayed.
* = Were ignoring a third, deprecated option: animate(Animation animation).

Animation from Resources


Back to the code: the first option is to pass an Android resource id pointing towards an animation resource. A simple example is the slide-in-left animation every Android system offers:
android.R.anim.slide_in_left. The code behind it is just an XML description of the animation:
1
2
3
4
5
6
7
8
9
10
11
12

<?xml version="1.0" encoding="utf-8"?>


<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-50%p"
android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>

Of course, you can create your own XML animations. For example, a little zoom in animation, which
starts the image small and then enlarges it to the full size:
66

Chapter 8 Glide Animations

1
2
3
4
5
6
7
8
9
10
11
12
13
14

67

<?xml version="1.0" encoding="utf-8"?>


<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="@android:integer/config_longAnimTime"
android:fromXScale="0.1"
android:fromYScale="0.1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1"/>
</set>

We could use either animation to set it in the Glide builder:


1
2
3
4
5

Glide
.with( context )
.load( eatFoodyImages[0] )
.animate( android.R.anim.slide_in_left ) // or R.anim.zoom_in
.into( imageView1 );

This will slide in the image from the left when the image is loaded from the network and ready to
go.

Animation via Custom Class


This works well when youre loading into regular ImageViews. But what if the target is something
custom, like weve talked about it in a previous section about custom views? Then the other options
are quite useful. Instead of passing a reference to an animation resource, you implement a class
against the ViewPropertyAnimation.Animator interface.
The interface is as simple as it gets, you just need to implement the void animate(View view)
method. The view object is the entire target view. If its a custom view, you can find the sub elements
of your view and do the necessary animations.
Lets look at a simple example. Lets assume you want to implement a fading animation programmatically, youd need to create the animator object:

Chapter 8 Glide Animations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

68

ViewPropertyAnimation.Animator animationObject = new ViewPropertyAnimation.Anima\


tor() {
@Override
public void animate(View view) {
// if it's a custom view class, cast it here
// then find subviews and do the animations
// here, we just use the entire view for the fade animation
view.setAlpha( 0f );
ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f \
);
fadeAnim.setDuration( 2500 );
fadeAnim.start();
}
};

Next, you need to set the animation at the Glide request:


1
2
3
4
5

Glide
.with( context )
.load( eatFoodyImages[1] )
.animate( animationObject )
.into( imageView2 );

Of course, in the animate(View view) of your animation object method, you can do anything you
want to do with the view. Feel free to get creative with your animation.
If youre working with a custom view, you can just cast the view object and then call custom methods
on your custom view.

Chapter Summary
In this chapter, youve learned how to create and apply standard and custom animations to your
Glide requests. This is one of the topics where playing around with it is very beneficial. We
recommend to take a few minutes to just test our code above and maybe implement one of your
own ideas. If you cant do that right now, at least make sure you understood the following:

[x] What options youve for requesting Glide to show animations


[x] How to apply an animation from the Android resources
[x] How to implement your own custom animation
[x] How to apply your own custom animation

Next, well start to tackle our last big Glide topic block: customizing Glide with Glide modules!

Chapter 9 Glide Modules


After learning about various options for loading and displaying images, well look at making changes
to the Glide core. Glide itself connects and controls multiple components, which can be adjusted.
In this chapter, well look at various options to customize the core behaviors of Glide. Before were
going to write a lot of custom code, well start easy by pre-made customizations.
Hint: This chapter assumes youre using Gradle.

Integrating Network Stacks


An important piece of displaying images from the Internet is downloading them via HTTP/HTTPS.
While the standard Android network packages do their job, there have been quite a few developments for improved networking on Android. Each library has its own advantages and disadvantages.
In the end, it comes down to project fit and the developers personal taste.
The developers of Glide dont want to force their preferred network library onto you, so Glide is
fairly HTTP/S network agnostic. Theoretically, it can work with any implementation, which fulfills
basic network capabilities. Integrating a network with Glide is not completely seamless. It requires
an interface setup of Glides ModelLoader. In order to make your life easier, Glide provides the
implementation for two network libraries: OkHttp and Volley.

OkHttp 2
Lets assume you want to integrate OkHttp 2 as the network library of your choice for Glide. The
integration can be done manually by declaring a GlideModule. If you want to avoid to do the
implementation by hand, just open your build.gradle and add the following two lines to your
dependencies:

http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/model/ModelLoader.html
https://github.com/square/okhttp
https://developer.android.com/training/volley/index.html

69

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9
10
11

70

dependencies {
// your other dependencies
// ...
// Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
// Glide's OkHttp Integration
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
compile 'com.squareup.okhttp:okhttp:2.7.5'
}

Gradle will automatically merge the necessary GlideModule into your Android.Manifest. Glide will
recognize the existence of it in the manifest and use OkHttp for all network connections.

Volley
On the other hand, if you rather use Volley, you have to change your build.gradle dependencies
to this:
1
2
3
4
5
6
7
8
9
10
11

dependencies {
// your other dependencies
// ...
// Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
// Glide's Volley Integration
compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar'
compile 'com.mcxiaoke.volley:library:1.0.8'
}

This will add Volley and the integration library into your project. The integration library adds the
GlideModule to your Android.Manifest. Glide will automatically recognize it there and use Volley
as the networking library. No further configuration is required!
Warning: if you declare both libraries in your build.gradle, both will get added. Since Glide
doesnt load them in any particular order, youll be an unstable situation, where its not clear which
networking library is picked. Make sure you only add one integration library.

OkHttp 3
If you want to use the new OkHttp 3 as the network stack, integrate it via the provided integration
library:

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9
10
11

71

dependencies {
// your other dependencies
// ...
// Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
// Glide's OkHttp3 Integration
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
}

Other Networking Libraries


If youre a fan of another networking library, youre out of luck. Glide does not come with an
automatic configuration besides Volley and OkHttp. However, feel free to integrate your favorite
networking library and open a pull request on GitHub. The implementation for Volley, OkHttp2
and OkHttp3 might give you a direction. Also, well learn all the necessary steps in the following
sections of this chapter.
As you can see, integrating the network libraries is fairly easy, if youre lucky enough to use Gradle
as your build system and dont want any further customization. If you dont use Gradle, please take
a look here. Next, well take a look at the GlideModule for even more advanced customizations.

Customize Glide with Modules


Weve looked at how you can set various network stacks for Glide to load your images. Internally,
the integration libraries for the network stacks are nothing else than declaring a GlideModule, which
is a way of significantly customize Glides behavior. In this section, well give you an overview over
GlideModules. Afterwards, for the rest of the chapter well go into examples of GlideModules.

Glide Modules
Glide modules are an abstract way of globally changing the Glides behavior. If you need access to the
GlideBuilder, which creates the Glide instance youre working with, this is the way to go. In order
to customize Glide, youll need to implement a public class against the GlideModule interface:

https://github.com/bumptech/glide/tree/3.0/integration
https://github.com/bumptech/glide/wiki/Integration-Libraries
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/module/GlideModule.html

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9

72

public class SimpleGlideModule implements GlideModule {


@Override public void applyOptions(Context context, GlideBuilder builder) {
// todo
}
@Override public void registerComponents(Context context, Glide glide) {
// todo
}
}

The interface offers you two methods to adjust different components of Glide. In this section, well
mostly look at the first method: applyOptions(Context context, GlideBuilder builder).
So you know youll have to create an extra class to customize Glide. Next up is to declare this class
globally, so Glide knows that it should load and use it. Glide will scan the AndroidManifest.xml for
meta declarations of Glide modules. Thus, youve to declare the just created Glide module in the
AndroidManifest.xml within the <application> tag:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

<manifest
...
<application>
<meta-data
android:name="io.futurestud.tutorials.glide.glidemodule.SimpleGlideM\
odule"
android:value="GlideModule" />
...
</application>
</manifest>

Make sure that you change the android:name property to your package name + class name, so its
referenced correctly. Thats it! You wont need to add another line to your code. If you want to
deactivate the Glide Module, just remove it from the AndroidManifest.xml. The Java class can stay
for later use. It wont be loaded or used when its not referenced in the AndroidManifest.xml.
The way Glide expects you to implement the customization modules has an advantage: you can
declare multiple Glide modules at the same time. Glide will go (in no particular order) through all
the declared modules. Since you currently cannot define the order, make sure that the customizations
dont cause conflicts!

Chapter 9 Glide Modules

73

GlideBuilder
Alright, youre aware of how to customize Glide using the Glide modules. Now lets look at the first
method of the interface: applyOptions(Context context, GlideBuilder builder). The method
gives you the GlideBuilder object as a variable. The method also has void as a return type, so all
you can do in this method is call the available methods on the GlideBuilder:

.setMemoryCache(MemoryCache memoryCache)
.setBitmapPool(BitmapPool bitmapPool)
.setDiskCache(DiskCache.Factory diskCacheFactory)
.setDiskCacheService(ExecutorService service)
.setResizeService(ExecutorService service)
.setDecodeFormat(DecodeFormat decodeFormat)

As you can see, the GlideBuilder object gives you access to significant core components of Glide.
Using the approach presented in this section, you can change the disk cache, memory cache and
more!
Well look at more advanced components later, but for now we pick the easiest change: .setDecodeFormat(DecodeFormat decodeFormat).
Example Usage: Increase Glides Image Quality
There are two main approaches to decode images on Android: ARGB8888 and RGB565. The first
uses 4 bytes for each pixel, the latter only two bytes for each pixel. ARG8888 has the advantage of
a higher image quality and the ability to store an alpha channel. While Picasso uses ARGB8888,
Glide defaults to the lesser quality of RGB565. The good news for Glide users: you can change the
decode format using the Glide module approach!
You simply need to implement a GlideModule, like weve shown you above, and then call
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888) with the correct enum value.

http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/GlideBuilder.html
https://futurestud.io/blog/tag/picasso/

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9

74

public class SimpleGlideModule implements GlideModule {


@Override public void applyOptions(Context context, GlideBuilder builder) {
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
@Override public void registerComponents(Context context, Glide glide) {
// nothing to do here
}
}

If youve followed our steps correctly, Glide will now use the higher image quality decoding.
The way to change other aspects of Glides behavior follow the same pattern. The code in the
registerComponents() will look a little bit different, but well look at it soon.
In this section, youve learned the basics of Glide modules. You should have a feeling when they can
be useful and how you can implement them. If you still need more information, see the resources
linked below. Especially if you need more clarification on how to utilize Glide modules in library
projects and/or are using ProGuard, make sure to review the content given there.
Since its a pretty complex topic, well go through another practical example: how to use a
GlideModule to enable access to images behind servers with self-signed HTTPS certificates (which
would not work by default) using the registerComponents() method!

Reading tip: GlideModules on Glides Github

Glide Module Example: Accepting Self-Signed HTTPS


Certificates
In the last couple of sections youve learned the basics of GlideModules. They provide an easy access
to some fundamental functions of Glides core. You can quickly modify Glides behavior by implementing and declaring GlideModules. Weve changed the image quality to a higher decode format
by utilizing the applyOptions() method. Next, well use the other method, registerComponents(),
to modify Glides network stack to accept connections and images from self-signed HTTPS servers.

Modifying Glide With GlideModule


Before continuing to read, please make sure you read and understood the previous sections on the
basics of GlideModules. We wont go over the basics again. Instead, we jump right to the issue. So
make sure youre up-to-date with the fundamentals of GlideModules.
https://github.com/bumptech/glide/wiki/Configuration

75

Chapter 9 Glide Modules

Youre aware that GlideModules offer you two methods to change the behavior. A little bit
ago, weve looked at the first method, applyOptions(). This time, well use the other method,
registerComponents(), to set a different network stack. By default, Glide internally uses the
standard HTTPUrlConnection to download images. Glide also offers two integration libraries. All
three of them are very strict in their security settings, which is very good. The only downside can
be the situation when your images are on a server, which uses HTTPS, but is self-signed. Glide would
not load or display the images, since the self-signed certificates are considered a security issue.

Unsafe OkHttpClient
Thus, youll need to implement your own network stack, which accepts self-signed certificates.
Luckily, weve implemented and used an unsafe OkHttpClient before. We can just copy and
paste the class, since it gives us a regular OkHttpClient, which well need to integrate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public class UnsafeOkHttpClient {


public static OkHttpClient getUnsafeOkHttpClient() {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain,
String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers()\
{
return null;
}
}
};
// Install the all-trusting trust manager
https://github.com/bumptech/glide/wiki/Integration-Libraries
https://futurestud.io/blog/picasso-customizing-picasso-with-picasso-builder/

Chapter 9 Glide Modules

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

76

final SSLContext sslContext = SSLContext.getInstance("SSL");


sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setSslSocketFactory(sslSocketFactory);
okHttpClient.setProtocols(Arrays.asList(Protocol.HTTP_1_1));
okHttpClient.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return okHttpClient;
}
}

The created OkHttpClient disables all SSL certificate checks.

Integration into Glide


Our advantage is that the OkHttp integration library for Glide does almost the same thing, so we
can follow their lead. First, well need to declare our customization in the GlideModule. As you
already expected, well need to do the adaption in the registerComponents() method. We can
call the .register() method to exchange some fundamental components of Glide. Glide uses a
ModelLoader to link a data model to a concrete data type. In our example, well need to create a
ModelLoader, which links an incoming URL, represented by the GlideUrl class to an InputStream.
Glide needs to be able to create instances of our new ModelLoader, so were passing a factory in the
.register() method:

http://bumptech.github.io/glide/javadocs/360/com/bumptech/glide/load/model/ModelLoader.html

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

77

public class UnsafeOkHttpGlideModule implements GlideModule {


@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
glide.register(
GlideUrl.class,
InputStream.class,
new OkHttpUrlLoader.Factory()
);
}
}

The first two parameters of the method are the model class and the linked resource class. The last
parameter is the ModelLoaderFactory. Consequently, we cant set an UnsafeOkHttpClient instance
directly, well need to create a ModelLoaderFactory, which provides the link between the URL and
the input stream using the UnsafeOkHttpClient.
Once again, luckily the OkHttp integration library gives us a good template:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {


/**
* The default factory for {@link OkHttpUrlLoader}s.
*/
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStr\
eam> {
private static volatile OkHttpClient internalClient;
private OkHttpClient client;
private static OkHttpClient getInternalClient() {
if (internalClient == null) {
synchronized (Factory.class) {
if (internalClient == null) {
internalClient =
UnsafeOkHttpClient.getUnsafeOkHttpClient();
}
}
https://github.com/bumptech/glide/wiki/Integration-Libraries

Chapter 9 Glide Modules

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

}
return internalClient;
}
/**
* Constructor for a new Factory that runs requests using a static singl\
eton client.
*/
public Factory() {
this(getInternalClient());
}
/**
* Constructor for a new Factory that runs requests using given client.
*/
public Factory(OkHttpClient client) {
this.client = client;
}
@Override
public ModelLoader<GlideUrl, InputStream> build(
Context context,
GenericLoaderFactory factories) {
return new OkHttpUrlLoader(client);
}
@Override
public void teardown() {
// Do nothing, this instance doesn't own the client.
}
}
private final OkHttpClient client;
public OkHttpUrlLoader(OkHttpClient client) {
this.client = client;
}
@Override
public DataFetcher<InputStream> getResourceFetcher(
GlideUrl model,
int width,

78

Chapter 9 Glide Modules

61
62
63
64

79

int height) {
return new OkHttpStreamFetcher(client, model);
}
}

In this class, you can see how the ModelLoaderFactory is constructed internally. For us, the
important line is the creation of the internalClient object: internalClient = UnsafeOkHttpClient.getUnsafeOkHttpClient();.
Unfortunately, we still need to link the URL to an active input stream using our unsafe OkHttpClient.
Thus, well need another class to fetch the response input stream for a URL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

public class OkHttpStreamFetcher implements DataFetcher<InputStream> {


private final OkHttpClient client;
private final GlideUrl url;
private InputStream stream;
private ResponseBody responseBody;
public OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
Request.Builder requestBuilder = new Request.Builder()
.url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()\
) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
Response response = client.newCall(request).execute();
responseBody = response.body();
if (!response.isSuccessful()) {
throw new IOException("Request failed: " + response.code());
}
long contentLength = responseBody.contentLength();

Chapter 9 Glide Modules

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

80

stream = ContentLengthInputStream.obtain(
responseBody.byteStream(),
contentLength
);
return stream;
}
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignored
}
}
if (responseBody != null) {
try {
responseBody.close();
} catch (IOException e) {
// Ignored.
}
}
}
@Override
public String getId() {
return url.getCacheKey();
}
@Override
public void cancel() {
// TODO: call cancel on the client when this method
// is called on a background thread. See #257
}
}

You dont need to understand the all the small details in this class. Nevertheless, you should have
an overview of the system Glide uses to replace internal factory components.
In this section, youve seen a different use case of changing the way Glide works. Weve implemented
an unsafe network stack and integrated it into Glide with the registerComponents() method of
a GlideModule. But thats just the tip of the iceberg of possible Glide modifications.

Chapter 9 Glide Modules

81

Next, well look at another option for a GlideModule to change Glides caching behavior.

Customize Memory Cache


In the last section, we went through the process of setting up our own Glide module to use a custom
HTTP client, which accepts self-signed HTTPS certificates. We stay on a low level and look at options
to customize the caching components of Glide.
Hopefully, youve read the chapters about caching and the previous sections in this chapter.
Otherwise some of the following code might look like magic to you. If youre ready to go, read
on.
Alright, since were customizing Glide, well need to create a new Glide module. As youve seen
in the previous section, the applyOptions method gives us access to the GlideBuilder object. The
GlideBuilder offers us several methods to customize Glides caching. First, lets look at the memory
cache.
The memory cache keeps the images in the devices RAM. There is no IO action going on and thus
its very fast. The flip side is that the size of the RAM is quite limited. Finding the right balance
of keeping a large memory cache (space for many images) versus a small memory cache (minimize
our apps hunger for resources) is not easy. Glide internally uses the MemorySizeCalculator class
to determine the size of the memory cache and the bitmap pool. The bitmap pool keeps the images
allocated in your apps heap. The correct size of the bitmap pool is essential as well, since it avoids
too many re-allocations of images and thus makes the garbage collectors life easier.
Luckily, youve access to Glides MemorySizeCalculator class and the default calculation:
1
2
3

MemorySizeCalculator calculator = new MemorySizeCalculator(context);


int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();

The code above is useful if we want to use the default values as baseline and adjust it from there.
For example, if you think your app needs 20% larger caches than Glides default values, calculate
them using our variables above:
1
2

int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);


int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);

Since weve figured out our memory cache and bitmap pool sizes, we can finally get to code our Glide
module. In the applyOptions() method, we can call the appropriate methods on the GlideBuilder
object:
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/engine/cache/MemorySizeCalculator.html

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

82

public class CustomCachingGlideModule implements GlideModule {


@Override public void applyOptions(Context context, GlideBuilder builder) {
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize);
int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);
builder.setMemoryCache( new LruResourceCache( customMemoryCacheSize );
builder.setBitmapPool( new LruBitmapPool( customBitmapPoolSize );
}
@Override public void registerComponents(Context context, Glide glide) {
// nothing to do here
}
}

As you can see from the last two lines in the applyOptions() method, we cant set the size directly.
Were creating an instance of the LruResourceCache and the LruBitmapPool class. Both are the
default implementations of Glide. Thus, if you only want to adjust the size, you can just continue to
use them by just passing a different size value to the constructor.

Customize Disk Cache


Adjusting the size of the disk cache works very similar, but weve one decision more to make. The
disk cache can be located in the apps private directory (in other words, no app has access to it,
except your own). Otherwise, the disk cache can also be located on the external, public directory
(for more information, see Storage Options. A combination of both locations is not possible
with the default settings. Glide offers an implementation for either one of the two options with
the InternalCacheDiskCacheFactory and ExternalCacheDiskCacheFactory. Just like the memory
cache constructor, both disk cache factories accept a size value in their constructor:

http://developer.android.com/guide/topics/data/data-storage.html

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

83

public class CustomCachingGlideModule implements GlideModule {


@Override
public void applyOptions(Context context, GlideBuilder builder) {
// set size & external vs. internal
int cacheSize100MegaBytes = 104857600;
builder.setDiskCache(
new InternalCacheDiskCacheFactory(
context,
cacheSize100MegaBytes
)
);
//builder.setDiskCache(
//
new ExternalCacheDiskCacheFactory(
//
context,
//
cacheSize100MegaBytes
//
)
//);
}
@Override
public void registerComponents(Context context, Glide glide) {
// nothing to do here
}
}

The code above would set the disk cache to the apps internal directory and also set the maximum
size to 100 megabytes. The line, which is behind the comment slashes, would set the disk cache to
the external directory (and also set the maximum size to 100 megabytes).
Both options youve seen above dont let you choose a specific directory. If you require to move the
disk cache to some specific location, you can utilize the DiskLruCacheFactory:
1
2
3
4
5
6
7
8
9

// or any other path


String downloadDirectoryPath = Environment.getDownloadCacheDirectory().getPath()\
;
builder.setDiskCache(
new DiskLruCacheFactory( downloadDirectoryPath, cacheSize100MegaBytes )
);
// In case you want to specify a cache sub folder (i.e. "glidecache"):

Chapter 9 Glide Modules

10
11
12
13
14
15
16

84

//builder.setDiskCache(
//
new DiskLruCacheFactory(
//
downloadDirectoryPath,
//
"glidecache",
//
cacheSize100MegaBytes
//
)
//);

Use the upper option to set a directory directly. The bottom option would create and use a directory
inside your passed directory path. As always, the last parameter is the size of the cache in bytes.

Custom Cache Implementations


So far, weve shown you how to move and set the cache to certain sizes. However, all calls referred
to the original implementation of the caches. What if you have your own cache implementation?
Well, youve seen we always created a new instance of Glides default cache implementation. You
can finish your own implementation, create and instance of it and pass it in all the methods youve
seen above. You just have to make sure that your cache code implements the interface methods:
Memory cache needs to implement: MemoryCache
Bitmap pool needs to implement BitmapPool
Disk cache needs to implement: DiskCache
In this section, youve seen how to change and customize Glides caches. Glides default implementation is well-rounded, so make sure youve reasons to change things. If you do change something,
make sure to test it on a range of devices!
Next, well look at another Glide module topic. Well look at how to implement a component, which
requests the images in the exact size of the target ImageView. We promise, itll be good!

Request Images in Certain Dimensions


Weve seen a bunch of various customizations for Glide using Glide modules. Well show you the last
example now, but possibly the most interesting one: how to request images in specific dimensions
from your server.
In a recent project we worked with a media server, which also served the images, that provided
the images in a very high resolution (images were like 6000x4500 pixels). While we could use direct
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/engine/cache/MemoryCache.html
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/engine/bitmap_recycle/BitmapPool.html
http://bumptech.github.io/glide/javadocs/latest/com/bumptech/glide/load/engine/cache/DiskCache.html

Chapter 9 Glide Modules

85

links to the source file, it was extremely inefficient regarding the devices bandwidth, memory and
battery. Even with the high-resolution displays of todays devices, there is no benefit of having such
an extremely high resolution. Thats why Glide always measures the dimensions of the ImageView
and then reduces the memory-allocated image to that size. However, the download and computation
overhead to reduce it to the smaller size are still there. Thus, the media server got a new feature: it
could serve the images in custom resolutions. Imagine it in the following way:
1
2
3
4
5
6
7

// previous way: we directly accessed the images


https://futurestud.io/images/logo.png
// new way, server could handle additional parameter and provide the image in a \
specific size
// in this case, the server would serve the image in 400x300 pixel size
https://futurestud.io/images/logo.png?w=400&h=300

The media server kept previously computed sizes on disk and, if not requested in the past, scale the
image on the fly. Now, the initial implementation on the Android side calculated the size of the ImageView, then made the Glide request with the concatenated URL (like ../logo.png?w=400&h=300),
like weve shown you above. This way worked, but is a little too complicated, especially if you
consider that Glide offers help here.

Another Custom GlideModule


Yes, of course, well have to declare a new Glide module. In this case, well have to register a new
model to Glide with the glide.register() method:
1
2
3
4
5
6
7
8
9
10
11
12
13

public class CustomImageSizeGlideModule implements GlideModule {


@Override public void applyOptions(Context context, GlideBuilder builder) {
// nothing to do here
}
@Override public void registerComponents(Context context, Glide glide) {
glide.register(
CustomImageSizeModel.class,
InputStream.class,
new CustomImageSizeModelFactory()
);
}
}

The .register() call configures Glide to understand all requests which are made against the
CustomImageSizeModel interface (instead of the regular GlideUrl interface). So this line has the

Chapter 9 Glide Modules

86

effect that you can create and pass instances of a CustomImageSizeModel implementation to Glide.
In order to handle this new custom model, well need to write a CustomImageSizeModelFactory
class, which creates an instance of our model request handler.
In summary, after youve added the Glide module from above, you should have two unknown
classes. The first is the CustomImageSizeModel:
1
2
3

public interface CustomImageSizeModel {


String requestCustomSizeUrl(int width, int height);
}
CustomImageSizeModel is just an interface, which adds the width and height as a parameter. That is

necessary so we can request pixel-exact images from our media server. The second unknown class
is the CustomImageSizeModelFactory:
1
2
3
4
5
6
7
8
9
10
11
12
13

private class CustomImageSizeModelFactory implements ModelLoaderFactory<CustomIm\


ageSizeModel, InputStream> {
@Override
public ModelLoader<CustomImageSizeModel, InputStream> build(Context context,\
GenericLoaderFactory factories) {
return new CustomImageSizeUrlLoader( context );
}
@Override
public void teardown() {
}
}

This class just follows Glides ModelLoaderFactory interface. It creates a new instance of our
CustomImageSizeUrlLoader, which takes care of loading the image once the Glide request was
created:
1
2
3
4
5
6
7
8
9
10
11

public class CustomImageSizeUrlLoader extends BaseGlideUrlLoader<CustomImageSize\


Model> {
public CustomImageSizeUrlLoader(Context context) {
super( context );
}
@Override
protected String getUrl(CustomImageSizeModel model, int width, int height) {
return model.requestCustomSizeUrl( width, height );
}
}

Chapter 9 Glide Modules

87

And with that our new Glide module is done and ready for custom size requests. Weve implemented
everything on the Glide module side, but weve not actually created an implementation of the
CustomImageSizeModel interface. In order to pass requests to Glide with the CustomImageSizeModel,
well need a class, which builds the custom image size URLs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public class CustomImageSizeModelFutureStudio implements CustomImageSizeModel {


String baseImageUrl;
public CustomImageSizeModelFutureStudio(String baseImageUrl) {
this.baseImageUrl = baseImageUrl;
}
@Override
public String requestCustomSizeUrl(int width, int height) {
// previous way: we directly accessed the images
// https://futurestud.io/images/logo.png
//
//
//
//

new way, server could handle additional parameter


and provide the image in a specific size
in this case, the server would serve the
image in 400x300 pixel size

// https://futurestud.io/images/logo.png?w=400&h=300
return baseImageUrl + "?w=" + width + "&h=" + height;
}
}

In the CustomImageSizeModelFutureStudio class above, weve implemented the logic to build the
image URL with the additional height and width parameters. Finally, we can create an instance of
this class and make a Glide request:
1
2
3
4
5
6
7
8

String baseImageUrl = "https://futurestud.io/images/example.png";


CustomImageSizeModel customImageRequest = new CustomImageSizeModelFutureStudio( \
baseImageUrl );
Glide
.with( context )
.load( customImageRequest )
.into( imageView2 );

As you can see above, we wont need to pass the exact dimensions. Glide will measure the ImageView
and pass it with our request. Now the server will respond with an image in the perfectly optimized
size!

Chapter 9 Glide Modules

88

Of course, you can just add additional CustomImageSizeModel model implementations, if youve
multiple servers, which use different logic to build the URL. Just create a new CustomImageSizeModel
implementation and pass it to your Glide request. You can use as many model implementations as
you need!

Section Summary
In this section, youve seen how to cut out a significant part of image request overhead. Every time
your users will see their battery status and data usage, theyll love you for it. Unfortunately, youll
need the support for it on the server side. Nevertheless, Glide makes the Android side very easy. The
initial setup is a little complex, but once you understood the concept, its very useful.
The issue with the approach weve shown you in this section: itll be used on every single request.
What if youve a mixed usage between image URLs, which can be resized, and image URLs, which
cannot be adjusted? In the next section, well show you how to apply the same idea dynamically on
a single request.

Dynamic Use of Model Loaders


In the last section, youve seen how to declare and configure a Glide module, which adds the image
size to the request. This is a very powerful optimization. However, its important to know that
declared Glide modules are always active. By default, you cannot dynamically turn them off or
on.
In this section, youll learn how to register a custom model loader on the fly for single requests.
Hint: If you havent read the previous section, make sure you do now. Otherwise the next part might
be confusing.
As a short review: usually Glide requests are made with the GlideUrl class. Weve shown you how
to create a new interface, which additionally takes the width and height into consideration:
1
2
3

public interface CustomImageSizeModel {


String requestCustomSizeUrl(int width, int height);
}

We created an implementation of it, which builds the passed image URL plus the dimensions to our
Future Studio server.

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9
10
11
12
13

89

public class CustomImageSizeModelFutureStudio implements CustomImageSizeModel {


String baseImageUrl;
public CustomImageSizeModelFutureStudio(String baseImageUrl) {
this.baseImageUrl = baseImageUrl;
}
@Override
public String requestCustomSizeUrl(int width, int height) {
return baseImageUrl + "?w=" + width + "&h=" + height;
}
}

Last, but not least, we had to create the CustomImageSizeUrlLoader, which passes the width and
height to our model implementation:
1
2
3
4
5
6
7
8
9
10
11

public static class CustomImageSizeUrlLoader extends BaseGlideUrlLoader<CustomIm\


ageSizeModel> {
public CustomImageSizeUrlLoader(Context context) {
super( context );
}
@Override
protected String getUrl(CustomImageSizeModel model, int width, int height) {
return model.requestCustomSizeUrl( width, height );
}
}

Glides Builder Method .using()


So far, weve declared a Glide module with the code above. Glide will use it for every single request.
If you dont want that, deactivate your Glide module by removing it from the AndroidManifest.xml.
We can do that because Glide offers the .using() method to specify a model for a single request:

Chapter 9 Glide Modules

1
2
3
4
5
6
7
8
9

90

String baseImageUrl = "https://futurestud.io/images/example.png";


CustomImageSizeModel customImageRequest = new CustomImageSizeModelFutureStudio( \
baseImageUrl );
Glide
.with( context )
.using( new CustomImageSizeUrlLoader( context ) )
.load( customImageRequest )
.into( imageView1 );

As you can see above, were creating a CustomImageSizeModelFutureStudio object for our image
in order to load it in its optimal size. Since weve not declared the CustomImageSizeModel interface
in a Glide module, we have to specify it in the line above with .using( new CustomImageSizeUrlLoader( context ) ). Glide will now use this model for this request only. All other requests, even
if they also are made with a CustomImageSizeModel instance, are not affected.
In this section, youve learned how to specify the request model usage for a specific request. If you
dont want to or cannot use Glide modules in the AndroidManifest this is an easy option for you.

Chapter Summary
Wow! That was a long chapter. Good job on working through all the complicated new content.
Glide modules have a steep learning curve, but can make things a lot easier for you, It can be very
beneficial for you to work through it again and attempt to utilize one or more of the tools youve
seen.
Lets review the content in this chapter. Youve learned:

[x] What Glide modules are


[x] What integration libraries are
[x] How to change the network component of Glide
[x] How to customize Cache sizes
[x] How to customize Cache implementations
[x] How to request images from the server in specific sizes
[x] How to apply model loaders dynamically

Congratulations, youve worked through the entire content of the book and have an extensive
knowledge about Glide now! In the next and final chapter, well look at how to use ProGuard and
how to configure it for the apps, which include Glide.

Chapter 10 App Release


Preparation
Google highly recommends to use ProGuard for release builds which get distributed via Google
Play. Well guide you through the basic setup of ProGuard and show you how to configure Glide in
the phase of release preparation.
First, lets get a wrap up about ProGuard and afterwards we dive into exemplary ProGuard rules to
keep your app working after compiling it with ProGuard enabled.

Enable ProGuard
First things first: What is ProGuard? ProGuard is a tool integrated with the Android build system
that shrinks, optimizes and obfuscates your app code by deleting unnecessary code and renaming
classes, methods and fields with cryptically names.
That sounds like there is some magic going on in the background when activating ProGuard.
Actually, the result of all this is a smaller sized .apk file that is much more difficult the reverse
engineer. ProGuard only runs when you build your app in release mode and additionally you need to
set the option minifyEnabled to true. The following code is an extract from the apps build.gradle.
1
2
3
4
5
6
7
8
9

android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}

The minifyEnabled true method activates ProGuard within the release build type. Additionally,
the proguardFiles getDefaultProguardFile('proguard-android.txt') fetches the default ProGuard settings from Androids SDK located within the tools/proguard folder. The proguardrules.pro file is located within your app folder and gets applied during the build phase as well.
https://developer.android.com/tools/help/proguard.html

91

Chapter 10 App Release Preparation

92

Configure ProGuard Rules for Glide


ProGuard is very harsh in regards to deleting code. Within many situations, ProGuard doesnt
analyze the code correctly and removes classes which seem to be unused, but are actually needed
in your app.
Getting a ClassNotFoundException within your app after running ProGuard is the indicator that
you need to configure ProGuard to keep the class(es). The general syntax to say the ProGuard tool
should keep a class:
1

-keep public class <MyClass>

Glide itself does not require any ProGuard configuration. However, if youre using any Glide module
configuration, like weve shown you in the last chapter, youll need to add a few lines. If you add a
Glide module, youll need to add the path and class name to your ProGuard file. For example, our
CustomCachingGlideModule would need the following setup:
1
2

-keepnames class * io.futurestud.tutorials.glide.glidemodule.CustomCachingGlideM\


odule

If you need multiple Glide modules, it might get messy. The creators recommend the following setup,
which catches all Glide modules:
1
2
3
4
5

-keep public class * implements com.bumptech.glide.module.GlideModule


-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}

The ProGuard configuration above may not fit completely for your app. Different apps may
require different ProGuard configurations to work properly when built in release mode. As already
mentioned, watch out for ClassNotFoundException and add entries within the proguard-rules.pro
file.

Obfuscated Stack Traces


Whenever ProGuard runs, it obfuscates your classes, methods and fields and its very hard to debug
your code. The good thing: ProGuard outputs a mapping.txt file which maps the original class,
method and field names with the obfuscated ones.
Use the retrace.sh on Mac and Linux or the retrace.bat on Windows to convert the obfuscated StackTrace into a comprehensible one. The respective file is located within <sdk_root>/tools/proguard/ directory. The syntax to execute the retrace tool is this:

Chapter 10 App Release Preparation

93

retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]

Precisely:
1

retrace.sh -verbose mapping.txt obfuscated_stacktrace.txt

Keep your mapping.txt file for every release you publish to users! Different app versions require
different mapping.txt files to translate possible errors back to understandable class, method and
field names.

Chapter Summary
This chapter explains how to configure your Android project for the use of ProGuard with Glide.
Keep in mind to save the mapping.txt files for stacktrace conversion and debugging. Error handling
with obfuscated code is hell on earth if you lost the mapping file and need to find the issue within
your code.

Outro
Our goal is to truly help you getting started and improve your knowledge around Glide. We hope
you learned many new things throughout this book. We want you to save time while learning the
basics and in-depth details about Glide. We think the existing Glide documentation lacks various
information and this book should help you to gain extensive in-depth knowledge without loosing
time searching StackOverflow for correct answers.
Did we miss something in this book? Do youve any feedback for us on your mind? Please take a
minute to write us an email to give us a chance to improve the book. Thank you!
This book based on Glide 3.7.0. The development team usually isnt afraid to make big changes, if
necessary. Currently, it looks like the version is stable. There is a 4.0 version in development, but we
dont know the current status of it. If the team releases a significant update, well update the book
accordingly. Please keep in mind though, that it might take some time to integrate all the changes.
Of course, everyone who bought this book will get the book update for free.
In the meantime, feel free to visit our homepage and blog. Were publishing new valuable posts
every Monday and Thursday about Android, Node.js and various open source tools.
Thanks a lot for reading this book! We highly appreciate your interest and hope you learned a lot
from this book! <3

mailto:info@futurestud.io
https://futurestud.io
https://futurestud.io/blog

94

Você também pode gostar