1. Software development
  2. An intro to Glide: Image loading for Android

An intro to Glide: Image loading for Android

typetalk

typetalk

July 24, 2018

Glide is an image loading library for Android that’s great at providing smooth, efficient scrolling for users. Though first developed by Bump Technologies, Google now owns and recommends it to users around the world. Google itself uses it in many open source projects including the Google I/O 2014 application.

Our dev team decided to try it out and ended up reducing our code significantly as a result. Now, we want to help other teams decide if Glide is right for them.

Why Glide?

There are several libraries that can achieve the same goals as Glide, but there is certainly a sense of security knowing that Google owns and uses it for its projects.

Plus, we found a number of benefits to choosing Glide over others, such as:

  • It’s simple. You only need to specify the image URL and the image view to display an image.
  • It automatically handles memory and file caching.
  • It supports GIF animations.

Choosing Glide worked for our team. And we think it can work for yours.

Getting started

The current version of Glide is 4.4.0, so this article will explain everything in terms of this version of the Glide library.

First, you need to add “glide” to your build.gradle dependencies.

1 implementation ‘com.github.bumptech.glide:glide:4.4.0’

Because we’re loading images from the Internet, we have to make sure to add those settings in AndroidManifest.xml.

1 <uses-permission android:name=”android.permission.INTERNET” />

Next, add ImageView to the layout file.

1

2

3

4

<ImageView

    android:id=”@+id/match_image”

    android:layout_width=”match_parent”

    android:layout_height=”wrap_content” />

Now, we can write the code for loading and displaying the image with onCreate from the Activity class.

1

2

3

ImageView matchImage = findViewById(R.id.match_image);

String gifUrl = “https://nulab.com/ja/app/uploads/sites/2/2017/10/techblog-vue-kanban-06.gif”;

Glide.with(this).load(gifUrl).into(matchImage);

Making the icons round

To use other functionalities, such as displaying the loaded image in a round shape, we need to use annotationProcessor. So we need to add annotationProcessor to the build.gradle dependencies, inherit the AppGlideModule, and add a Java class with a GlideModule annotation.

1 annotationProcessor ‘com.github.bumptech.glide:compiler:4.4.0’

MyAppGlideModule.java

1

2

3

4

5

6

7

8

package com.example.myapp;

 

import com.bumptech.glide.annotation.GlideModule;

import com.bumptech.glide.module.AppGlideModule;

 

@GlideModule

public class MyAppGlideModule extends AppGlideModule {

}

Add the ImageView for icons to the layout file.

1

2

3

4

<ImageView

    android:id=”@+id/icon”

    android:layout_width=”40dp”

    android:layout_height=”40dp”/>

Instead of Glide.with(this) we can now use GlideApp.with(this), so we only need to add a circleCrop() in between to make the icons round.

1

2

3

ImageView iconImage = findViewById(R.id.icon);

String iconUrl = “https://typetalk.com/accounts/5/profile_image.png”;

GlideApp.with(this).load(iconUrl).circleCrop().into(iconImage);

Image loading size

You’ll see an output like this when we look at the application log.

Glide treats LayoutParams.WRAP_CONTENT as a request for an image fitted to the screen dimensions of the current device.. If you want to load the original image and can afford the increased memory cost and OOMs (depending on the input size), use .override(Target.SIZE_ORIGINAL). Otherwise, use LayoutParams.MATCH_PARENT, set layout_width and layout_height to fixed dimension, or use .override() with fixed dimensions.

When using WRAP_CONTENT, ImageView tries to load the image in accordance with the screen size of the device, so if you want to load it at its original size, it’s best if you either assign .override(Target.SIZE_ORIGINAL), specify MATCH_PARENT for the image size, or specify a fixed size with layout_width and layout_height or .override.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<LinearLayout

    android:layout_width=”match_parent”

    android:layout_height=”wrap_content”

    android:orientation=”vertical”

    android:gravity=”center_horizontal”>

    <ImageView

        android:id=”@+id/match_image”

        android:layout_width=”match_parent”

        android:layout_height=”wrap_content”

        android:adjustViewBounds=”true”/>

    <ImageView

        android:id=”@+id/wrap_image”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:adjustViewBounds=”true”/>

    <ImageView

        android:id=”@+id/w100_image”

        android:layout_width=”100dp”

        android:layout_height=”wrap_content”

        android:adjustViewBounds=”true”/>

</LinearLayout>

Load the same image with a different ImageView, view the log output using RequestListener, and check the bitmap size.

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

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

 

    RequestManager rm = Glide.with(this);

    String imageUrl = “https://nulab.com/ja/app/uploads/sites/2/2017/12/FullSizeRender-11-e1513846838609.jpg”;

    rm.load(imageUrl)

            .listener(createLoggerListener(“match_image”))

            .into((ImageView)findViewById(R.id.match_image));

    rm.load(imageUrl)

            .listener(createLoggerListener(“wrap_image”))

            .into((ImageView)findViewById(R.id.wrap_image));

    rm.load(imageUrl)

            .listener(createLoggerListener(“w100_image”))

            .into((ImageView)findViewById(R.id.w100_image));

}

 

private RequestListener<Drawable> createLoggerListener(final String name) {

    return new RequestListener<Drawable>(){

 

        @Override

        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {

            return false;

        }

 

        @Override

        public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {

            if (resource instanceof BitmapDrawable) {

                Bitmap bitmap = ((BitmapDrawable) resource).getBitmap();

                Log.d(“GlideApp”,

                        String.format(“Ready %s bitmap %,d bytes, size: %d x %d”,

                                name,

                                bitmap.getByteCount(),

                                bitmap.getWidth(),

                                bitmap.getHeight()));

            }

            return false;

        }

    };

}

 

Ready w100_image bitmap 256,800 bytes, size: 300 x 214

Ready match_image bitmap 3,330,720 bytes, size: 1080 x 771

Ready wrap_image bitmap 9,007,872 bytes, size: 1776 x 1268

The bitmap size does get bigger when you apply it to the ImageView we specified wrap_content for; be careful and monitor your memory.

You can specify the size of an image using Override.

1

2

3

4

5

GlideApp.with(this)

    .load(imageUrl)

    .listener(createLoggerListener(“override200”))

    .override(200, 200)

    .into(matchImage);

 

Ready override200 bitmap 114,400 bytes, size: 200 x 143

Loading images when we don’t know their size

In some cases, the type of the image being loaded may not be known beforehand. It could be a photo or an icon — handling them appropriately is important. For example, loading a small icon into a large ImageView meant for a photo may have good rendering performance, but an expanded bitmap will be created, eating into your available memory and cache.

1

2

3

Glide.with(this).load(iconUrl)

        .listener(createLoggerListener(“match image”))

        .into((ImageView) findViewById(R.id.match_image));

 

Ready match image bitmap 4,665,600 bytes, size: 1080 x 1080

In a situation like this, try assigning scaleType=”centerInside” in the ImageView. With that, it won’t expand beyond the size of the original image.

1

2

3

4

5

<ImageView

    android:id=”@+id/center_image”

    android:layout_width=”match_parent”

    android:layout_height=”wrap_content”

    android:scaleType=”centerInside”/>

 

1

2

3

Glide.with(this).load(iconUrl)

        .listener(createLoggerListener(“center image”))

        .into((ImageView) findViewById(R.id.center_image));

 

Ready center image bitmap 65,536 bytes, size: 128 x 128

It’s also possible to expand images without using ImageView attributes, by specifying options in the program as shown below.

1

2

3

4

5

GlideApp.with(this).load(iconUrl)

        .downsample(DownsampleStrategy.CENTER_INSIDE)

        .dontTransform()

        .listener(createLoggerListener(“set downsample”))

        .into((ImageView) findViewById(R.id.wrap_image));

 

Ready set downsample bitmap 65,536 bytes, size: 128 x 128

It is advised to set these kinds of options. Not doing so may result in unpredictable and inconsistent behavior, since image sizing will depend entirely on ImageView attribute values.

Make a request with an HTTP Header

By using GlideUrl, we can make requests with a request header. In this example, we will display an image from files attached to Typetalk.

1

2

3

4

5

String protectedUrl = “https://typetalk.com/api/v1/topics/xxx/posts/xxxxxxxxxx/attachments/1/sample.jpg”;

Headers headers = new LazyHeaders.Builder()

        .addHeader(“Authorization”, “Bearer XXXXXXXXXXXXXXXXXXXXXXXX”)

        .build();

Glide.with(this).load(new GlideUrl(protectedUrl, headers)).into(matchImage);

Displaying thumbnails

You can specify a thumbnail request separately. In this example, we’ve set the request to “thumbnail,” since we can get a small image if we add the type=small parameter to the API obtained from the files attached to Typetalk. By doing this, we can show a clearer, higher-resolution image after first loading a small image.

Suppose we have a main screen and a second “details” screen. After the smaller image is displayed at the initial, main screen, it is cached. At that point, if we open the details screen, both the small version and the original higher-res version can be shown.

1

2

3

4

5

6

7

8

GlideRequests gr = GlideApp.with(this);

gr.load(new GlideUrl(protectedUrl, headers))

        .thumbnail(gr

                .load(new GlideUrl(protectedUrl + “?type=small”, headers))

                .override(Target.SIZE_ORIGINAL)

                .listener(createLoggerListener(“small”)))

        .listener(createLoggerListener(“original”))

        .into(matchImage);

 

Ready small bitmap 691,200 bytes, size: 480 x 360

Ready original bitmap 3,499,200 bytes, size: 1080 x 810

Final thoughts

Glide has many other features outside the scope of a single post, so anyone who is interested should check out the documentation for things like:

  • Setting images during loading (placeholder)
  • Setting images when an error occurs (error)
  • Animations such as fade-in during the time the image loads (transition)
  • Disabling the memory cache and disk cache
  • Loading only from the cache
  • SVG display (sample)

Thank you for reading! Hopefully, this brief introduction to Glide can help your team take one step closer to fast, efficient scrolling!

Related

Subscribe to our newsletter

Learn with Nulab to bring your best ideas to life