Coil — este o bibliotecă pentru încărcarea imaginilor pe Android, scrisă în Kotlin și construită pe bază de corutine. Conform documentației oficiale, biblioteca suportă Memory Cache, Disk Cache și transformări cu accelerare hardware. Coil se evidențiază prin dimensiunea minimă a APK-ului (aproximativ 150 KB) și compatibilitatea completă cu Jetpack Compose.
Principalele puncte
Coil (Coroutine Image Loader) — este o bibliotecă pentru încărcarea imaginilor pe Android, scrisă integral în Kotlin și care utilizează corutinele pentru lucru asincron. Oferă o API unificată pentru încărcarea imaginilor bitmap din rețea, resurse, sistem de fișiere și Content Provider, cu memorare automată în cache la mai multe niveluri.
Spre deosebire de Glide și Picasso, Coil folosește Kotlin Coroutines în locul lanțurilor de callback, ceea ce face codul mai liniar și mai previzibil. Toate operațiile de încărcare și decodare sunt executate pe fire de fundal prin Dispatchers.IO, iar rezultatul este livrat în firul principal fără comutare explicită.
Coil suportă transformări (Round, Blur, Grayscale), animații de tranziție, SVG și GIF, precum și Target personalizat pentru afișare non-standard. Conform Google I/O 2023, Coil este recomandat în tutorialele oficiale Jetpack Compose alături de Glide.
ImageLoader — componenta principală a Coil, responsabilă pentru executarea cererilor de încărcare și gestionarea cache-ului. Fiecare instanță conține referințe la MemoryCache, DiskCache, BitmapPool și un pool de corutine. În mod implicit, se folosește un singleton creat prin Coil.imageLoader(context).
ImageRequest — obiect care descrie o singură cerere de încărcare a imaginii: sursa de date (URL, URI, resursă Int), ImageView sau Target de destinație, transformări, setări de cache și placeholder. ImageRequest este construit printr-un builder, ceea ce asigură flexibilitate și lizibilitate.
val request = ImageRequest.Builder(context)
.data("https://example.com/image.jpg")
.crossfade(true)
.size(512, 512)
.transformations(listOf(RoundedCornersTransformation(12f)))
.memoryCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(CachePolicy.ENABLED)
.target(imageView)
.build()
După construire, ImageRequest este transmis către ImageLoader prin enqueue sau execute. Metoda enqueue lansează o corutină și returnează un Disposable, permițând anularea încărcării la părăsirea ecranului. Metoda execute este o funcție suspend care returnează Result direct.
ImageLoader verifică succesiv MemoryCache, DiskCache și doar la lipsa ambelor execută o cerere de rețea prin HttpEngine. După încărcare, octeții sunt decodați în Bitmap ținând cont de dimensiunea țintă, se aplică transformările, rezultatul este salvat în ambele cache-uri și transmis către Target.
Coil este construit pe o arhitectură componentă cu posibilitatea de a înlocui orice parte prin Dependency Injection. Toate componentele sunt înregistrate în ImageLoaderFactory și transmise în constructorul ImageLoader prin builder.
ImageLoader — punctul de intrare pentru toate operațiile de încărcare. Fiecare instanță conține un pool de corutine, BitmapPool, MemoryCache, DiskCache și o listă de interceptori. În mod implicit, se creează o instanță globală, dar pentru testare modulară se pot crea instanțe separate cu cache izolat.
MemoryCache — cache în memorie bazat pe LRU (Least Recently Used) care stochează obiecte Bitmap decodate. Dimensiunea maximă implicită este 25% din memoria disponibilă a aplicației, dar nu mai puțin de 32 MB. Cheia cache-ului este formată din URL + dimensiune + transformări, ceea ce exclude furnizarea unei imagini neactualizate.
DiskCache — cache de fișiere pentru date brute (JPEG, PNG, WebP) și metadate decodate. Se află în directorul de cache al aplicației și suportă curățarea automată la depășirea limitei. Lucrul cu discul se execută prin DiskCache.Builder cu configurarea directorului și a dimensiunii maxime.
Coil implementează o strategie de cache pe mai multe niveluri, minimizând cererile de rețea și accelerând afișarea imaginilor. Fiecare nivel are propriul scop și durata de viață a datelor.
| Nivel | Tip de stocare | Durata de viață | Dimensiune implicită |
|---|---|---|---|
| Memory Cache | Bitmap în memorie | Până la înlocuirea LRU | 25% heap, de la 32 MB |
| Disk Cache | Fișiere JPEG/WebP | Până la depășirea limitei | 250 MB |
| Http Cache | Răspunsuri OkHttp | După antetele Cache-Control | Depinde de clientul HTTP |
Memory Cache asigură acces instantaneu la bitmap-urile deja decodate. Disk Cache garantează funcționarea aplicației fără rețea (offline-first) după prima încărcare. Http Cache la nivelul OkHttp procesează cererile condiționate ETag și If-Modified-Since.
Politicile de cache se configurează per-cerere prin CachePolicy cu trei valori: ENABLED, READ_ONLY, WRITE_ONLY, DISABLED. De exemplu, pentru avatarurile utilizatorilor se poate seta READ_ONLY pentru Memory Cache și ENABLED pentru Disk Cache.
Coil oferă mai multe moduri de integrare în funcție de arhitectura aplicației. Să analizăm trei scenarii cheie cu exemple de cod funcționale.
load — funcție extensie pentru ImageView, cel mai simplu mod de a încărca o imagine într-o singură linie. Funcția acceptă URL, URI, resursă Int sau File și toți parametrii opționali printr-un configurator lambda.
imageView.load("https://example.com/photo.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
error(R.drawable.error)
size(300, 300)
transformations(CircleCropTransformation())
}
Metoda load returnează un Disposable care poate fi anulat în onDestroy sau la reutilizarea View-ului. Acest lucru previne scurgerile de memorie și cererile de rețea inutile la derularea rapidă a listei.
AsyncImage — funcție composable pentru încărcarea imaginilor în UI declarativ. Acceptă orice sursă de date și trei parametri opționali pentru stări: placeholder, error și success.
@Composable
fun NetworkImage(url: String) {
AsyncImage(
model = url,
contentDescription = "imagine de rețea",
placeholder = ColorPainter(Color.Gray),
error = ColorPainter(Color.Red)
)
}
SubcomposeAsyncImage — o versiune mai flexibilă care permite personalizarea afișării în timpul încărcării printr-un slot de conținut. Acest lucru este util pentru schelete (shimmer) și bare de progres.
Dacă ImageView sau AsyncImage nu sunt potrivite, se poate implementa Target cu o singură metodă onSuccess care primește Bitmap. Acest lucru este utilizat pentru încărcarea în Notification, RemoteViews sau texturi OpenGL.
val target = object : BitmapTarget() {
override fun onSuccess(result: Bitmap) {
notificationRemoteView.setImageViewBitmap(R.id.icon, result)
}
}
imageLoader.enqueue(
ImageRequest.Builder(context)
.data(url)
.target(target)
.build()
)
Alegerea bibliotecii pentru încărcarea imaginilor depinde de cerințele proiectului. Coil concurează cu Glide și Picasso, fiecare având puncte forte. Compararea caracteristicilor principale este prezentată în tabel.
| Caracteristică | Coil | Glide | Picasso |
|---|---|---|---|
| Limbaj | Kotlin (100%) | Java + Kotlin | Java |
| Dimensiunea APK | ~150 KB | ~500 KB | ~120 KB |
| Corutine | Încorporate | Nu (callback) | Nu (callback) |
| Jetpack Compose | Suport nativ | Prin Accompaniment | Terță parte |
| GIF/WebP | Da (încorporat) | Da (încorporat) | Nu |
| Recomandare Google | Da (I/O 2023) | Da | Nu |
Pentru proiecte noi în Kotlin și Jetpack Compose, Coil devine alegerea naturală datorită dependenței zero suplimentare de corutine și dimensiunii minime. Glide rămâne preferat pentru scenarii complexe cu animații și previzualizări video. Picasso este inferior ambelor din punct de vedere funcțional, dar câștigă în simplitate.
Conectarea Coil la proiectul Android se face prin dependența Gradle. După adăugare, biblioteca înregistrează automat ImageLoader prin ContentProvider, deci inițializarea manuală în Application nu este necesară. La nevoie de personalizare, se creează propriul ImageLoader prin builder.
// build.gradle.kts (modulul aplicației)
dependencies {
implementation("io.coil-kt:coil:2.6.0")
// Adițional pentru Jetpack Compose:
implementation("io.coil-kt:coil-compose:2.6.0")
// Pentru suport SVG:
implementation("io.coil-kt:coil-svg:2.6.0")
// Pentru suport GIF:
implementation("io.coil-kt:coil-gif:2.6.0")
}
Pentru personalizarea ImageLoader se folosește ImageLoaderFactory — un singleton creat în Application.onCreate. În fabrică se pot configura limitele de cache, clientul HTTP, decodoare personalizate și logarea. În mod implicit, Coil folosește OkHttp cu un pool de conexiuni gata făcut.
class App : Application(), ImageLoaderFactory {
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this)
.memoryCache {
MemoryCache.Builder()
.maxSizePercent(0.25)
.build()
}
.diskCache {
DiskCache.Builder()
.directory(cacheDir.resolve("coil_cache"))
.maxSizeBytes(512 * 1024 * 1024)
.build()
}
.build()
}
}
Întrebări frecvente
Coil — bibliotecă pentru încărcarea imaginilor pe Android, scrisă în Kotlin cu utilizarea corutinelor. Este folosită pentru încărcarea asincronă, memorarea în cache și afișarea imaginilor bitmap din rețea, resurse sau sistemul de fișiere.
Coil este scris 100% în Kotlin și folosește corutine în locul mecanismului de callback din Glide. Coil are o dimensiune mai mică a APK-ului (~150 KB față de ~500 KB) și suport nativ pentru Jetpack Compose prin AsyncImage.
Adăugați dependența io.coil-kt:coil:2.6.0 în build.gradle.kts. Pentru Jetpack Compose adăugați și io.coil-kt:coil-compose:2.6.0. Biblioteca înregistrează automat ImageLoader prin ContentProvider.
Coil suportă JPEG, PNG, WebP, BMP, SVG (prin modulul coil-svg) și GIF (prin modulul coil-gif). Formatele AVIF și HEIF sunt suportate printr-un decodor personalizat pe dispozitive cu Android 10+.
Cache-ul se configurează prin ImageLoader.Builder: memoryCache cu specificarea procentului din heap, diskCache cu calea și limita în octeți. Politicile de cache (ENABLED, DISABLED, READ_ONLY) se configurează per-cerere prin CachePolicy.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și