Skip to main content
Android Development with Kotlin
CHAPTER 25 Beginner

Image Handling with Glide/Coil

Updated: May 16, 2026
20 min read

# CHAPTER 25

Image Handling with Glide/Coil

1. Introduction

In modern mobile development, rich visual media is non-negotiable. E-commerce apps, social feeds, and news aggregates all rely heavily on displaying high-resolution images. However, downloading a 4-Megabyte image from a URL, decoding the bitmap, and rendering it in an ImageView is a mathematically intensive process. If you attempt this manually on the Main Thread, the app will freeze. If you attempt to load 50 high-res images in a RecyclerView simultaneously, the device will exhaust its RAM, crashing the app with the dreaded OutOfMemoryError (OOM). To solve this, the Android ecosystem relies on powerful asynchronous Image Loading Libraries. In this chapter, we will master Image Handling utilizing Coil (and touch on the legacy Glide), exploring network fetching, disk caching, and memory optimization.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the complexities and memory constraints of Android Bitmap rendering.
  • Explain the architectural advantages of utilizing dedicated image loading libraries.
  • Configure and integrate the Coil library into an Android Kotlin project.
  • Load remote images from URLs directly into an ImageView efficiently.
  • Implement placeholder graphics and error fallbacks.
  • Apply dynamic image transformations (e.g., circular cropping).

3. The Problem: OutOfMemoryError

An image that takes up 2MB of disk space might expand to 20MB of raw memory when decompressed into an uncompressed Bitmap for display on a 4K phone screen. If a user scrolls fast through a list of 100 images, Android will try to allocate 2,000MB of RAM. The OS will immediately kill your app. Image loading libraries solve this via Downsampling (shrinking the image in memory before drawing it) and aggressive Caching (saving it to disk so it doesn't need to be downloaded twice).

4. Glide vs. Coil

  • Glide: Built by Bumptech (supported by Google). It has been the industry standard for Java/Kotlin apps for nearly a decade. It is robust but utilizes an older annotation-processor architecture.
  • Coil (Coroutine Image Loader): The modern standard. Built 100% in Kotlin, utilizing Coroutines under the hood. It is incredibly lightweight, blazingly fast, and pairs perfectly with modern Kotlin codebases and Jetpack Compose. We will focus on Coil.

5. Step 1: Integrating Coil

First, ensure you have the Internet permission in your AndroidManifest.xml (as images are fetched from the web): <uses-permission android:name="android.permission.INTERNET" />

Next, add the Coil dependency to your build.gradle.kts (Module :app):

kotlin
1
implementation("io.coil-kt:coil:2.5.0")

*Click "Sync Now"!*

6. Step 2: Basic Image Loading

With Coil, loading an image from the internet into an ImageView takes exactly *one line of code*. Coil utilizes a powerful Kotlin extension function on the ImageView class called .load().
kotlin
1234567891011121314151617
import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import coil.load // Import the Coil extension function!

class ProfileActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_profile)

        val profileImageView = findViewById<ImageView>(R.id.profileImage)
        val imageUrl = "https://images.unsplash.com/photo-1511367461989-f85a21fda167"

        // Magic! Coil downloads it in the background, caches it, and displays it!
        profileImageView.load(imageUrl)
    }
}

7. Step 3: Placeholders and Fallbacks

Network requests take time. If the user has a slow 3G connection, the ImageView will be blank for 5 seconds. This is bad UX. We must provide a local Placeholder image to show *while* loading, and an Error image to show if the URL is broken or the internet disconnects.
kotlin
123456
profileImageView.load(imageUrl) {
    crossfade(true) // Applies a smooth fade-in animation when the image arrives
    crossfade(500)  // Fade duration in milliseconds
    placeholder(R.drawable.ic_loading_spinner) // Show this WHILE downloading
    error(R.drawable.ic_broken_image)          // Show this if the download FAILS
}

8. Step 4: Transformations

Often, user avatars need to be displayed as perfect circles. Doing this manually via XML or custom Bitmaps is a nightmare. Coil provides built-in transformations.
kotlin
1234567891011
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation

profileImageView.load(imageUrl) {
    crossfade(true)
    // Transforms the downloaded square image into a perfect circle!
    transformations(CircleCropTransformation())
    
    // OR, for rounded rectangular corners:
    // transformations(RoundedCornersTransformation(radius = 30f))
}

9. Coil in a RecyclerView

When utilizing Coil inside a RecyclerView adapter, you do absolutely nothing special. Inside your onBindViewHolder, you simply call holder.imageView.load(url). Coil is intelligent enough to know that if the user scrolls the image completely off the screen *before* the download finishes, it will automatically cancel the background Coroutine network request to save bandwidth and battery!

10. Common Mistakes

  • Forgetting Internet Permissions: As always, attempting to fetch an HTTP/HTTPS image without the Manifest permission will silently fail or throw a security exception, triggering your Coil error fallback immediately.
  • Loading Massive Images into Tiny Views: If you download a 4000x4000 pixel image to display in a 50x50 ImageView, Coil handles the downsampling nicely. However, if you force the ImageView to wrap content, it might try to render the massive bitmap. Ensure your XML ImageView has strict layout_width and layout_height dimensions (e.g., 100dp) so Coil knows exactly how small to compress the image in memory.

11. Best Practices

  • Preloading: If you know the user is about to navigate to a screen containing a heavy hero image, you can tell Coil to execute the download in the background *before* the screen opens, utilizing imageLoader.enqueue(request). When the user finally opens the screen, the image appears instantly from the cache.

12. Exercises

  1. 1. Add the Coil dependency to your project.
  1. 2. Place a temporary ImageView (width/height 200dp) in your MainActivity. Use Coil to load a random image from https://picsum.photos/400 into it. Apply the CircleCropTransformation.

13. Coding Challenges

Challenge: Build an "Avatar Gallery". Create a RecyclerView with a GridLayoutManager (3 columns). Create a list of 30 random image URLs. Bind the URLs utilizing Coil inside the adapter. Apply a placeholder. Scroll rapidly up and down the list to verify that Coil handles the caching and lifecycle management seamlessly without stuttering or crashing.

14. MCQ Quiz with Answers

Question 1

When architecting an Android application that renders multiple high-resolution remote images, what is the primary computational vulnerability of executing manual HttpURLConnection and BitmapFactory.decodeStream() logic?

Question 2

Within the Coil image loading DSL, what is the specific architectural purpose of defining a placeholder() resource?

15. Interview Questions

  • Q: Explain the mechanical mechanics of "Downsampling" within an image loading library. Why is evaluating the target ImageView's physical dimensions (via measure()) a prerequisite for efficient memory allocation?
  • Q: Contrast the underlying concurrency architectures of Glide and Coil. How does Coil's reliance on Kotlin Coroutines provide a structural advantage over legacy thread-pool implementations?
  • Q: Detail the lifecycle awareness of modern image loaders within a RecyclerView. What happens to an active network download request if the target ViewHolder is rapidly scrolled off-screen?

16. Summary

In Chapter 25, we conquered the memory-intensive complexities of rich media rendering. We recognized the fatal vulnerabilities of manual Bitmap decoding (OOM errors) and integrated the modern, Coroutine-backed Coil library. We executed asynchronous image fetching via elegant extension functions, enhancing UX through Placeholders, Error Fallbacks, and Crossfade Animations. Finally, we leveraged programmatic Transformations to dynamically format UI assets, ensuring our applications remain visually stunning, highly performant, and memory-safe.

17. Next Chapter Recommendation

Our application logic, networking, and UI are now enterprise-grade. However, before releasing an app, we must ensure it behaves predictably. We cannot manually test 50 different edge cases every time we change a line of code. Proceed to Chapter 26: Unit Testing and UI Testing to master Automated Testing and secure your codebase against regressions.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·