Escolar Documentos
Profissional Documentos
Cultura Documentos
Android
2015 - 2016 Norman Peitek
Contents
About the Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
What Topics are Covered in this Book? . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Who is this book for? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i
i
i
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
1
2
3
5
7
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8
8
12
14
24
25
26
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
28
28
30
32
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
36
37
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
39
39
42
43
49
.
.
.
.
CONTENTS
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
50
50
51
52
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
53
53
60
63
64
66
66
68
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
69
69
71
74
81
82
84
84
88
90
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
91
91
92
92
93
Outro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
94
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
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.
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>
http://en.wikipedia.org/wiki/Fluent_interface
http://developer.android.com/reference/android/content/Context.html
1
2
3
4
5
6
7
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.
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
The small helper function is a simple conversion from the resourceId to an Uri.
1
2
3
4
5
6
7
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.
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 );
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).
Glide
.with( context )
.load( gifUrl )
.asBitmap()
.into( imageViewGifAsBitmap );
This should give you all the knowledge to display Gifs with Glide. Its easy, try it!
1
2
3
4
5
6
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:
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.
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
Third, lets look at the layout files for the adapter. The layout file for a ListView item is very simple:
1
2
3
4
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
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
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.
11
12
13
14
Now, we just have to adjust the adapter weve used a bit ago. Our new adapter code:
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
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().
17
18
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
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
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
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:
23
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.
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
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:
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:
27
But were not done with learning details about Glide yet. In the next chapter, well look at image
resizing and scaling.
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
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
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.
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/
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.
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
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!
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.
33
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:
1
2
3
4
5
6
35
Glide
.with( context )
.load( eatFoodyImages[0] )
.diskCacheStrategy( DiskCacheStrategy.NONE )
.skipMemoryCache( true )
.into( imageViewInternet );
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/
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.
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!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
37
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
38
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.
SimpleTarget
So lets look at a code example:
39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
40
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.
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
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.
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
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
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.
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!
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
a custom notification.
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
46
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
Lastly, we need to call Glide itself and as weve done in the previous posts, set the target as the
.into() parameter:
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
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:
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.
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
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
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
51
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:
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!
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.
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
54
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
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/
56
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!
57
58
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!
59
60
Hint: You cannot use .centerCrop() or .fitCenter() when you use transformations. Otherwise
your transformation will not be applied!
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
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
62
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.
dependencies {
compile 'jp.wasabeef:glide-transformations:2.0.0'
}
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
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.
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/
65
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.
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).
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
67
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
68
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:
Next, well start to tackle our last big Glide topic block: customizing Glide with Glide modules!
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
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:
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'
}
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
1
2
3
4
5
6
7
8
9
72
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!
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/
1
2
3
4
5
6
7
8
9
74
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!
75
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
76
http://bumptech.github.io/glide/javadocs/360/com/bumptech/glide/load/model/ModelLoader.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
77
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
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
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
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.
81
Next, well look at another option for a GlideModule to change Glides caching behavior.
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
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
82
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.
http://developer.android.com/guide/topics/data/data-storage.html
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
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
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.
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
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.
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
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
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
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
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
// 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
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!
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.
We created an implementation of it, which builds the passed image URL plus the dimensions to our
Future Studio server.
1
2
3
4
5
6
7
8
9
10
11
12
13
89
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
1
2
3
4
5
6
7
8
9
90
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:
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.
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
92
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
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
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.
93
Precisely:
1
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