Fallback Image Loading over Network using Glide 4

/ Android / 3 min read

Backups are always important. You can't rely on a single source to ensure an excellent user experience for your apps.

While crafting one of our latest projects, BookWritten for Android, we wanted to ensure one thing - an user sees the book cover even if it's a low resolution image. Having something up there was better than showing a placeholder.

As complex as it topic might sound, it's actually pretty simple to implement this system using Glide 4. We'll dive deep into that but before let's find out the motivation behind all of this.

Why use a fallback?

BookWritten relies on Goodreads to provide a plethora of books at our user's disposal. While adding Goodreads to the app, we have noticed that Goodreads is most likely to return a medium size image for book covers.

Well, medium in their terms but it's actually pretty small. It's 95px X 160px to be exact.

With more and more mobile phones going full HD, an image that small, appears pretty blown up on the device. Interestingly, Goodreads also provides a larger version of the same book cover, about 282px X 475px in dimensions but not guaranteed to be present for every book and everytime.

We couldn't build the app based on an "almost". There had to be a backup plan.

Here comes the fallback:

We decided to try loading the high resolution image first and iff it fails to load, we'll fall back to a lower resolution image. A lower resolution is better than nothing.

Setting up the fallback image loading

We used Glide 4 to achieve this system with a few lines of code. Yes, it's really that simple. Thanks, Google.

First of all, you need Glide 4. If you haven't already added Glide to your project (can't seem to find a reason) you can add it by placing this dependency on your build.gradle file:

repositories {
  mavenCentral()
  google()
}

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.6.1'
  annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
}

After that's done, you're all set to use the magical powers of Glide to supercharge your image loading on Android.

Now, Glide provides an error() method to tell you when it has failed to load an image from the source to your ImageView. We'll be leveraging that method to build our fallback system.

First and foremost, you need to decide on which image to use as the regular one and which one to choose as a fallback. In our case, higher resolution image was tried first and then switched to fallback which was the lower resolution image, if the first one didn't load.

After you've done that, you can set up fallback image loading with this piece of code:

Glide.with(context)
            .load(imageUrl)
            .error(Glide.with(context).load(fallbackImageUrl))
            .into(imageView)

Here, on error() we're passing a new image loading request, which will be called iff the initial loading has failed. Sweet!

If you're using Java, then that's all you need. However, if you're using Kotlin, there's one more trick left up the sleeve.

Simplifying it further with Kotlin

At Upcurve, we're a sucker for beautiful and concise code. Kotlin helps us do that and that's why we're in a relationship with the language.

If you're using Kotlin for coding your Android app, which you should, we can simplify this loading by writing an extension function. If you're yet to catch-up with Kotlin, go ahead and read this article on why you should start using Kotlin.

All we need to do is to hide that ugly code behind a sexy extension function for ImageView so that we won't need to write 4 lines of code everytime we need to load an image.

So, let's create an extension function for ImageView named loadImage() and use Glide to do the image loading on the ImageView. Something like this:

fun ImageView.loadImage(imageUrl: String, fallbackImageUrl: String) =
    Glide.with(context)
            .load(imageUrl)
            .error(Glide.with(context).load(fallbackImageUrl))
            .into(this)

Here, the second parameter is the fallback image URL. Whenever you need to load an image with a fallback, you can now do it with a single line of code, like this:

imageView.loadImage(imageUrl, fallbackImageUrl)

Handy, isn't it?

That's the beauty of this language.

Now, chances are that you won't be needing to use a fallback image everywhere. How do you load image then? Using the 4 liner patent Glide code?

Nopes. Just create another overloaded extension function without the fallback URL parameter, like this:

fun ImageView.loadImage(imageUrl: String) =
        Glide.with(context)
                .load(imageUrl)
                .into(this)

Done and dusted.

Final thoughts

Getting things done is good. Getting things done in a failsafe way is so much better.

If your plan isn't working, adjust your plan. Never give up. - Matt Martin

Design your app in such a way that it's less likely to screw up in front of your customer. After all, it's built to ease and please the customer, isn't it?