How It's Made

Introducing Coil: Kotlin-first Image Loading on Android

I’m excited to announce Coil — an open-source Kotlin-first image loading library for Android. Coil is fast, lightweight, modern, and treats Kotlin Coroutines, OkHttp, Okio, and AndroidX Lifecycles as first-class citizens. Coil is an acronym for Coroutine Image Loader.

With hundreds of millions of catalog listings, we load a lot of images at Instacart. On the Android team, our choice of image loading library is one of the most important parts of our app.

Here’s what Coil looks like:

// To load an image into an ImageView, use the load extension function.
imageView.load("https://www.example.com/image.jpg")

// Coil supports urls, uris, resources, drawables, bitmaps, files, and more.
imageView.load(R.drawable.image)
imageView.load(File("/path/to/image.jpg"))
imageView.load("content://com.android.externalstorage/image.jpg")

// Requests can be configured with an optional trailing lambda.
imageView.load("https://www.example.com/image.jpg") {
    crossfade(true)
    placeholder(R.drawable.image)
    transformations(CircleCropTransformation())
}

// Internally, ImageView.load() is powered by the singleton ImageLoader.
val imageLoader = context.imageLoader

// Optionally, you can create and inject your own instance(s).
val imageLoader = ImageLoader(context)

// Enqueue an ImageRequest to load the image asynchronously into a custom target.
val request = ImageRequest.Builder(context)
    .data("https://www.example.com/image.jpg")
    .target { drawable ->
        // Handle the successful result.
    }
    .build()
imageLoader.enqueue(request)

// Execute an ImageRequest to suspend and return the drawable.
val request = ImageRequest.Builder(context)
    .data("https://www.example.com/image.jpg")
    .build()
val drawable = imageLoader.execute(request).drawable

While there are several image loading library options for Android developers today, we felt there was an opportunity to create a more modern, simpler product. Coil was created with the following goals:

  • Leverage Kotlin language features including extension functions, inlining, lambda params, and sealed classes to create a simple, elegant API.
  • Leverage Kotlin Coroutines as they offer strong support for non-blocking asynchronous computation and work cancellation while maximizing thread reuse.
  • Use modern dependencies. Square’s OkHttp and Okio are standard dependencies for every Android app. They’re efficient by default and allow Coil to avoid reimplementing disk caching and stream buffering. Similarly, AndroidX Lifecycles are now the recommended way to track lifecycle state; Coil is the only image loading library that supports them.
  • Lightweight. Coil has almost 8x fewer lines of codethan Glide and slightly less than Picasso. Also, Coil adds ~1500 methods to your APK (for apps that already use OkHttp and Coroutines), which is comparable to Picasso and significantly less than Glide and Fresco.
  • Support extensions. Coil’s image pipeline is composed of three main classes Mappers, Fetchers, and Decoders. These interfaces can be used to augment and/or override the base behavior and add support for new file types in Coil.
  • Improve testing. Coil’s main service class, ImageLoader, is an interface. This allows you to write fake ImageLoader implementations for your tests. Coil also supports dependency injection, as it provides singleton and non-singleton artifacts.
  • Avoid annotation processing, which can often slow down build speeds. Coil relies on Kotlin extension functions instead.

We’ve worked hard to make sure Coil covers the existing functionality supported by other image loaders, but we also wanted to throw in an additional unique feature:

 


List images have intentionally been loaded in low res and the crossfade has been slowed down to highlight the effect.

Suppose you have an image that is 500×500 on disk, but is being loaded into an ImageView that is 100×100. Coil will load the image into memory at 100×100.

However, what happens now if you need the image at 500×500? There’s still more “quality” to read from disk, but the image is already loaded into memory at 100×100. Ideally, we would use the 100×100 image as a placeholder while we read the image from disk at 500×500.

This is exactly what Coil does and Coil handles this process automatically for all BitmapDrawables (and soon all Drawables). Paired with a crossfade animation, this can create a pleasant visual effect where the image detail appears to fade in, similar to a progressive JPEG. The placeholder is also set synchronously on the main thread, which prevents white flashes where the ImageView is empty for one frame.

We’re extremely excited to be sharing Coil with the community. There’s much more to talk about that we couldn’t fit into this post so check out the documentationand the Github repository. You can also find me on Twitter here.

We’re currently using Coil at Instacart and we recommend you give the library a try as well.

We’ll also be releasing a benchmarking repository soon, which compares Coil’s runtime performance against other image loading libraries.

Interested in projects like Coil? Check out Instacart’s current engineering openings here.

Thanks to Kaushik Gopal.

Colin White

Author

Most Recent in How It's Made

One Model to Serve Them All: How Instacart deployed a single Deep Learning pCTR model for multiple surfaces with improved operations and performance along the way

How It's Made

One Model to Serve Them All: How Instacart deployed a single Deep Learning pCTR model for multiple surfaces with improved operations and performance along the way

Authors: Cheng Jia, Peng Qi, Joseph Haraldson, Adway Dhillon, Qiao Jiang, Sharath Rao Introduction Instacart Ads and Ranking Models At Instacart Ads, our focus lies in delivering the utmost relevance in advertisements to our customers, facilitating novel product discovery and enhancing…

Dec 19, 2023
Monte Carlo, Puppetry and Laughter: The Unexpected Joys of Prompt Engineering

How It's Made

Monte Carlo, Puppetry and Laughter: The Unexpected Joys of Prompt Engineering

Author: Ben Bader The universe of the current Large Language Models (LLMs) engineering is electrifying, to say the least. The industry has been on fire with change since the launch of ChatGPT in November of…

Dec 19, 2023
Unveiling the Core of Instacart’s Griffin 2.0: A Deep Dive into the Machine Learning Training Platform

How It's Made

Unveiling the Core of Instacart’s Griffin 2.0: A Deep Dive into the Machine Learning Training Platform

Authors: Han Li, Sahil Khanna, Jocelyn De La Rosa, Moping Dou, Sharad Gupta, Chenyang Yu and Rajpal Paryani Background About a year ago, we introduced the first version of Griffin, Instacart’s first ML Platform, detailing its development and support for end-to-end ML in…

Nov 22, 2023