Coil has 5 artifacts published to mavenCentral()
:
io.coil-kt:coil
: The default artifact which depends onio.coil-kt:coil-base
and includes theCoil
singleton and theImageView
extension functions.io.coil-kt:coil-base
: The base artifact which does not include theCoil
singleton and theImageView
extension functions.io.coil-kt:coil-gif
: Includes two decoders to support decoding GIFs. See GIFs for more details.io.coil-kt:coil-svg
: Includes a decoder to support decoding SVGs. See SVGs for more details.io.coil-kt:coil-video
: Includes two fetchers to support fetching and decoding frames from any of Android's supported video formats. See videos for more details.
You should depend on io.coil-kt:coil-base
and not io.coil-kt:coil
if either of the following is true:
- You are writing a library that depends on Coil. This is to avoid opting your users into the singleton.
- You want to use dependency injection to inject your ImageLoader instance(s).
If you need transformations that aren't part of the base Coil artifact, check out the third-party coil-transformations
library hosted here.
Coil requires Java 8 bytecode. To enable this feature add the following to your Gradle build script:
Gradle (.gradle
):
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
Gradle Kotlin DSL (.gradle.kts
):
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
ImageLoader
s are service classes that execute ImageRequest
s. ImageLoader
s handle caching, data fetching, image decoding, request management, bitmap pooling, memory management, and more. New instances can be created and configured using a builder:
val imageLoader = ImageLoader.Builder(context)
.availableMemoryPercentage(0.25)
.crossfade(true)
.build()
Coil performs best when you create a single ImageLoader
and share it throughout your app. This is because each ImageLoader
has its own memory cache, bitmap pool, and network observer.
It's recommended, though not required, to call shutdown
when you've finished using an image loader. Calling shutdown
preemptively frees its memory and cleans up any observers. If you only create and use a single ImageLoader
, you do not need to shut it down as it will be freed when your app is killed.
ImageRequest
s are value classes that are executed by ImageLoader
s. They describe where an image should be loaded from, how it should be loaded, and any extra parameters. An ImageLoader
has two methods that can execute a request:
enqueue
: Enqueues theImageRequest
to be executed asynchronously on a background thread.execute
: Executes theImageRequest
in the current coroutine and returns anImageResult
.
All requests should set data
(i.e. url, uri, file, drawable resource, etc.). This is what the ImageLoader
will use to decide where to fetch the image data from. If you do not set data
, it will default to NullRequestData
.
Additionally, you likely want to set a target
when enqueuing a request. It's optional, but the target
is what will receive the loaded placeholder/success/error drawables. Executed requests return an ImageResult
which has the success/error drawable.
Here's an example:
// enqueue
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
.target(imageView)
.build()
val disposable = imageLoader.enqueue(request)
// execute
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
.build()
val result = imageLoader.execute(request)
If you are using the io.coil-kt:coil
artifact, you can set the singleton ImageLoader
instance by either:
- Implementing
ImageLoaderFactory
on yourApplication
class (prefer this method):
class MyApplication : Application(), ImageLoaderFactory {
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(context)
.crossfade(true)
.okHttpClient {
OkHttpClient.Builder()
.cache(CoilUtils.createDefaultCache(context))
.build()
}
.build()
}
}
Or calling Coil.setImageLoader
:
val imageLoader = ImageLoader.Builder(context)
.crossfade(true)
.okHttpClient {
OkHttpClient.Builder()
.cache(CoilUtils.createDefaultCache(context))
.build()
}
.build()
Coil.setImageLoader(imageLoader)
The singleton ImageLoader
can be retrieved using the Context.imageLoader
extension function:
val imageLoader = context.imageLoader
Setting the singleton ImageLoader
is optional. If you don't set one, Coil will lazily create an ImageLoader
with the default values.
If you're using the io.coil-kt:coil-base
artifact, you should create your own ImageLoader
instance(s) and inject them throughout your app with dependency injection. Read more about dependency injection here.
!!! Note
If you set a custom OkHttpClient
, you must set a cache
implementation or the ImageLoader
will have no disk cache. A default Coil cache instance can be created using CoilUtils.createDefaultCache
.
The io.coil-kt:coil
artifact provides a set of type-safe ImageView
extension functions. Here's an example for loading a URL into an ImageView
:
imageView.load("https://www.example.com/image.jpg")
The above call is equivalent to:
val imageLoader = imageView.context.imageLoader
val request = ImageRequest.Builder(imageView.context)
.data("https://www.example.com/image.jpg")
.target(imageView)
.build()
imageLoader.enqueue(request)
ImageView.load
calls can be configured with an optional trailing lambda parameter:
imageView.load("https://www.example.com/image.jpg") {
crossfade(true)
placeholder(R.drawable.image)
transformations(CircleCropTransformation())
}
See the docs here and here for more information.
The base data types that are supported by all ImageLoader
instances are:
- String (mapped to a Uri)
- HttpUrl
- Uri (
android.resource
,content
,file
,http
, andhttps
schemes only) - File
- @DrawableRes Int
- Drawable
- Bitmap
To preload an image into memory, enqueue or execute an ImageRequest
without a Target
:
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
// Optional, but setting a ViewSizeResolver will conserve memory by limiting the size the image should be preloaded into memory at.
.size(ViewSizeResolver(imageView))
.build()
imageLoader.enqueue(request)
To preload a network image only into the disk cache, disable the memory cache for the request:
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
.memoryCachePolicy(CachePolicy.DISABLED)
.build()
imageLoader.enqueue(request)
ImageRequest
s will be automatically cancelled in the following cases:
request.lifecycle
reaches theDESTROYED
state.request.target
is aViewTarget
and itsView
is detached.
Additionally, ImageLoader.enqueue
returns a Disposable, which can be used to dispose the request (which cancels it and frees its associated resources):
val disposable = imageView.load("https://www.example.com/image.jpg")
// Cancel the request.
disposable.dispose()
Each ImageLoader
has its own MemoryCache
of recently loaded images. To read/write a Bitmap
to the memory cache, you need a MemoryCache.Key
. There are two ways to get a MemoryCache.Key
:
- Create a
MemoryCache.Key
using itsString
constructor:MemoryCache.Key("my_cache_key")
- Get the
MemoryCache.Key
from an executed request:
// If using the ImageLoader singleton
val memoryCacheKey = imageView.metadata.memoryCacheKey
// Enqueue
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
.target(imageView)
.listener { _, metadata ->
val memoryCacheKey = metadata.memoryCacheKey
}
.build()
imageLoader.enqueue(request)
// Execute
val request = ImageRequest.Builder(context)
.data("https://www.example.com/image.jpg")
.build()
val result = imageLoader.execute(request) as SuccessResult
val memoryCacheKey = result.metadata.memoryCacheKey
Once you have the memory cache key, you can read/write to the memory cache synchronously:
// Get
val bitmap: Bitmap? = imageLoader.memoryCache[memoryCacheKey]
// Set
imageLoader.memoryCache[memoryCacheKey] = bitmap