一、一句话理解 Coil
Coil 是专为 Kotlin 和 Jetpack ***pose 设计的图片加载库,基于协程(Coroutine)和 OkHttp,API 简洁到极致,一行代码搞定图片加载。
它的名字来自 Coroutine Image Loader 的缩写 —— 从名字就能看出它的基因:协程 + 轻量 + 现代化。
二、为什么选 Coil?—— 对比 Glide / Picasso
| 特性 | Glide | Picasso | Coil |
|---|---|---|---|
| 语言 | Java(Kotlin 可用) | Java | ✅ 纯 Kotlin |
| 异步机制 | 自定义线程池 | 自定义线程池 | ✅ 基于 Kotlin 协程 |
| Jetpack ***pose 支持 | 需额外封装 | 不支持 | ✅ 官方原生支持 |
| 包体积 | 较大(~500KB) | 中等(~300KB) | ✅ 更小(~300KB,且可裁剪) |
| API 风格 | 链式调用 | 链式调用 | ✅ 函数式 + DSL,极度简洁 |
| 内存管理 | 强大 | 一般 | ✅ 自动生命周期感知 + 协程取消 |
📌 如果你用 Kotlin + ***pose,Coil 是目前最自然的选择!
三、快速上手:三行代码加载图片
步骤1️⃣:添加依赖(build.gradle)
dependencies {
implementation("io.coil-kt:coil:2.7.0")
// 如果用 Jetpack ***pose
implementation("io.coil-kt:coil-***pose:2.7.0")
// 如果需要 GIF 支持
implementation("io.coil-kt:coil-gif:2.7.0")
}
步骤2️⃣:在 View 中加载(传统 XML 布局)
val imageView: ImageView = findViewById(R.id.imageView)
// 一行加载网络图片!
imageView.load("https://example.***/image.jpg") {
placeholder(R.drawable.placeholder)
error(R.drawable.error_icon)
crossfade(true)
transformations(CircleCropTransformation()) // 圆形裁剪
}
✅ 自动:
- 内存/磁盘缓存
- 生命周期感知(Activity 销毁时自动取消)
- 协程后台解码
步骤3️⃣:在 Jetpack ***pose 中加载(推荐!)
@***posable
fun ProfileImage(url: String) {
AsyncImage(
model = url,
contentDescription = "User avatar",
modifier = Modifier
.size(100.dp)
.clip(CircleShape),
placeholder = painterResource(R.drawable.placeholder),
error = painterResource(R.drawable.error_icon)
)
}
🔥 无需
ImageView,直接在 ***posable 中加载!
四、Coil 核心优势详解
✅ 1. 协程原生支持
Coil 的所有操作都在协程中执行,天然支持结构化并发:
lifecycleScope.launch {
val drawable = coil.imageLoader(context).execute(
ImageRequest.Builder(context)
.data("https://example.***/image.jpg")
.build()
).drawable
imageView.setImageDrawable(drawable)
}
💡 如果协程被取消(如页面关闭),图片加载自动停止,零内存泄漏风险!
✅ 2. 强大的变换(Transformations)
内置多种图像处理,也可自定义:
imageView.load(url) {
transformations(
BlurTransformation(context, radius = 10f),
GrayscaleTransformation(),
RoundedCornersTransformation(radius = 16f)
)
}
自定义变换也很简单:
class MyCustomTransformation : Transformation {
override fun key(): String = "MyCustom"
override suspend fun transform(input: Bitmap, size: Size): Bitmap {
return input.copy(Bitmap.Config.ARGB_8888, true).apply {
// 自定义绘制逻辑
}
}
}
✅ 3. 智能缓存策略
Coil 使用两级缓存:
- 内存缓存(MemoryCache):LRU,快速访问
- 磁盘缓存(DiskCache):基于 OkHttp 的缓存,支持 HTTP 缓存头
你可以自定义:
val imageLoader = ImageLoader.Builder(context)
.diskCache {
DiskCache.Builder()
.directory(context.cacheDir.resolve("image_cache"))
.maxSizeBytes(100L * 1024 * 1024) // 100MB
.build()
}
.build()
// 全局设置(Application 中)
Coil.setImageLoader(imageLoader)
✅ 4. 完美支持 ***pose
这是 Coil 最大的杀手锏!
// 加载并自动适配大小
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(user.avatarUrl)
.size(200, 200) // 解码时就缩放到目标尺寸,省内存!
.build(),
contentDescription = null,
modifier = Modifier.fillMaxWidth(),
contentScale = ContentScale.Crop
)
✅
AsyncImage会:
- 自动暂停/恢复(***pose 生命周期感知)
- 支持
placeholder/error/onSu***ess/onError- 与
LazyColumn完美配合(自动取消不可见项的加载)
五、高级用法:自定义请求 & 拦截器
场景:统一添加 Token 到图片 URL(如私有 CDN)
val imageLoader = ImageLoader.Builder(context)
.***ponents {
add(OkHttpInterceptor***ponentFactory { _, _ ->
Interceptor { chain ->
val request = chain.request()
if (request.url.host == "private-cdn.example.***") {
val newUrl = request.url.newBuilder()
.addQueryParameter("token", getAuthToken())
.build()
chain.proceed(request.newBuilder().url(newUrl).build())
} else {
chain.proceed(request)
}
}
})
}
.build()
六、性能优化技巧
✅ 1. 指定尺寸(避免加载大图)
imageView.load(url) {
size(500, 500) // 告诉 Coil 只需解码到 500x500
}
减少内存占用高达 90%!
✅ 2. 使用 crossfade 提升体验
imageView.load(url) {
crossfade(true) // 渐显动画
crossfade(300) // 自定义时长
}
✅ 3. 预加载(Preload)
// 在列表滑动前预加载下一页
imageLoader.enqueue(ImageRequest.Builder(context)
.data(nextImageUrl)
.memoryCachePolicy(CachePolicy.DISABLED) // 只进磁盘缓存
.build())
七、常见问题 & 最佳实践
❓ Q1:Coil 支持 WebP / GIF 吗?
✅ 支持!但 GIF 需要额外依赖:
implementation("io.coil-kt:coil-gif:2.7.0")
❓ Q2:如何监听加载成功/失败?
imageView.load(url) {
listener(
onSu***ess = { request, metadata ->
Log.d("Image", "Loaded: ${metadata.size}")
},
onError = { request, throwable ->
Log.e("Image", "Failed", throwable)
}
)
}
✅ 最佳实践1:不要在 RecyclerView ViewHolder 中直接 load
✅ 正确做法:在 onBindViewHolder 中加载,并确保 URL 变化时取消旧请求(Coil 自动处理!)
✅ 最佳实践2:全局配置一次 ImageLoader
在 Application 中初始化,统一 placeholder、缓存、拦截器等。
八、总结一句话
Coil 是为 Kotlin 和 ***pose 而生的现代图片加载库:它用协程驱动、API 极简、生命周期安全,并深度集成 Jetpack ***pose,让你用最少的代码,实现最流畅的图片体验。
从 ImageView.load() 到 AsyncImage,一行代码,图片到位。
💡 小练习:用 Coil 实现一个带圆角、占位图、错误图的 ***pose 图片组件
@***posable
fun ***workImage(
url: String?,
modifier: Modifier = Modifier,
contentDescription: String? = null
) {
AsyncImage(
model = url,
contentDescription = contentDescription,
modifier = modifier.clip(RoundedCornerShape(12.dp)),
placeholder = painterResource(R.drawable.ic_image_placeholder),
error = painterResource(R.drawable.ic_image_error),
contentScale = ContentScale.Crop
)
}
是不是清爽又强大?