Coil — 是一个用于在 Android 上加载图片的库,用 Kotlin 编写并基于协程构建。根据官方文档,该库支持 Memory Cache、Disk Cache 以及硬件加速的变换。Coil 以其最小的 APK 大小(约 150 KB)和与 Jetpack Compose 的完全兼容性而脱颖而出。
要点
Coil(Coroutine Image Loader)— 是一个用于在 Android 上加载图片的库,完全用 Kotlin 编写并使用协程进行异步操作。它提供了一个统一的 API,用于从网络、资源、文件系统和 Content Provider 加载位图图像,并具有自动的多级缓存。
与 Glide 和 Picasso 不同,Coil 使用 Kotlin Coroutines 代替回调链,使代码更加线性和可预测。所有加载和解码操作通过 Dispatchers.IO 在后台线程执行,结果不需要显式切换即可传递到主线程。
Coil 支持变换(Round、Blur、Grayscale)、过渡动画、SVG 和 GIF,以及用于非标准显示的自定义 Target。根据 Google I/O 2023,Coil 与 Glide 一起被推荐在官方的 Jetpack Compose 教程中使用。
ImageLoader — Coil 的主要组件,负责执行加载请求和管理缓存。每个实例包含对 MemoryCache、DiskCache、BitmapPool 和协程池的引用。默认情况下使用通过 Coil.imageLoader(context) 创建的单例。
ImageRequest — 描述单个图片加载请求的对象:数据源(URL、URI、Int 资源)、目标 ImageView 或 Target、变换、缓存设置和占位图。ImageRequest 通过构建器构建,提供了灵活性和可读性。
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()
构建完成后,ImageRequest 通过 enqueue 或 execute 传递给 ImageLoader。enqueue 方法启动一个协程并返回 Disposable,允许在离开屏幕时取消加载。execute 方法是一个挂起函数,直接返回 Result。
ImageLoader 依次检查 MemoryCache、DiskCache,只有当两者都未命中时才通过 HttpEngine 执行网络请求。加载完成后,字节根据目标大小解码为 Bitmap,应用变换,结果保存到两个缓存中并传递给 Target。
Coil 基于组件化架构构建,可以通过 依赖注入 替换任何部分。所有组件都在 ImageLoaderFactory 中注册,并通过构建器传递给 ImageLoader 的构造函数。
ImageLoader — 所有加载操作的入口点。每个实例包含协程池、BitmapPool、MemoryCache、DiskCache 和拦截器列表。默认情况下创建一个全局实例,但对于模块化测试,可以创建具有隔离缓存的单独实例。
MemoryCache — 基于 LRU(最近最少使用)的内存缓存,存储解码后的 Bitmap 对象。默认最大大小是应用程序可用内存的 25%,但不少于 32 MB。缓存键由 URL + 大小 + 变换组成,排除了提供过时图像的可能性。
DiskCache — 用于原始数据(JPEG、PNG、WebP)和解码后元数据的文件缓存。位于应用程序的缓存目录中,支持超出限制时的自动清理。磁盘操作通过 DiskCache.Builder 执行,配置目录和最大大小。
Coil 实现了多级缓存策略,最大限度地减少网络请求并加快图像显示。每个级别都有自己的目的和数据生命周期。
| 级别 | 存储类型 | 生命周期 | 默认大小 |
|---|---|---|---|
| Memory Cache | 内存中的 Bitmap | 直到 LRU 淘汰 | 25% 堆,从 32 MB 起 |
| Disk Cache | JPEG/WebP 文件 | 直到超过限制 | 250 MB |
| Http Cache | OkHttp 响应 | 根据 Cache-Control 头 | 取决于 HTTP 客户端 |
Memory Cache 提供对已解码位图的即时访问。Disk Cache 确保应用程序在首次加载后无需网络即可工作(离线优先)。Http Cache 在 OkHttp 级别处理条件请求 ETag 和 If-Modified-Since。
缓存策略通过 CachePolicy 按请求配置,具有三个值:ENABLED、READ_ONLY、WRITE_ONLY、DISABLED。例如,对于用户头像,可以为 Memory Cache 设置 READ_ONLY,为 Disk Cache 设置 ENABLED。
Coil 根据应用架构提供了多种集成方式。让我们通过工作代码示例来看三个关键场景。
load — ImageView 的扩展函数,是用一行代码加载图片的最简单方式。该函数接受 URL、URI、Int 资源或 File,并通过 lambda 配置器接受所有可选参数。
imageView.load("https://example.com/photo.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
error(R.drawable.error)
size(300, 300)
transformations(CircleCropTransformation())
}
load 方法返回一个 Disposable,可以在 onDestroy 中或 View 被重用时取消。这可以防止快速滚动列表时的内存泄漏和不必要的网络请求。
AsyncImage — 用于在声明式 UI 中加载图片的可组合函数。接受任何数据源和三个可选的状态参数:placeholder、error 和 success。
@Composable
fun NetworkImage(url: String) {
AsyncImage(
model = url,
contentDescription = "网络图片",
placeholder = ColorPainter(Color.Gray),
error = ColorPainter(Color.Red)
)
}
SubcomposeAsyncImage — 更灵活的版本,允许通过内容插槽自定义加载过程中的显示。这对于骨架屏(shimmer)和进度条非常有用。
如果 ImageView 或 AsyncImage 不合适,可以实现只有一个 onSuccess 方法的 Target,该方法接受 Bitmap。这用于加载到 Notification、RemoteViews 或 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()
)
图片加载库的选择取决于项目需求。Coil 与 Glide 和 Picasso 竞争,每个都有自己的优势。主要特性的比较如下表所示。
| 特性 | Coil | Glide | Picasso |
|---|---|---|---|
| 语言 | Kotlin(100%) | Java + Kotlin | Java |
| APK 大小 | ~150 KB | ~500 KB | ~120 KB |
| 协程 | 内置 | 否(回调) | 否(回调) |
| Jetpack Compose | 原生支持 | 通过 Accompaniment | 第三方 |
| GIF/WebP | 是(内置) | 是(内置) | 否 |
| Google 推荐 | 是(I/O 2023) | 是 | 否 |
对于 Kotlin 和 Jetpack Compose 的新项目,Coil 由于零额外协程依赖和最小尺寸成为自然选择。Glide 仍然是复杂场景(如动画和视频预览)的首选。Picasso 在功能上不如两者,但在简单性上胜出。
将 Coil 连接到 Android 项目通过 Gradle 依赖完成。添加后,库通过 ContentProvider 自动注册 ImageLoader,因此不需要在 Application 中手动初始化。如果需要自定义,通过构建器创建自己的 ImageLoader。
// build.gradle.kts(应用模块)
dependencies {
implementation("io.coil-kt:coil:2.6.0")
// Jetpack Compose 额外添加:
implementation("io.coil-kt:coil-compose:2.6.0")
// 用于 SVG 支持:
implementation("io.coil-kt:coil-svg:2.6.0")
// 用于 GIF 支持:
implementation("io.coil-kt:coil-gif:2.6.0")
}
用于自定义 ImageLoader 的是 ImageLoaderFactory — 在 Application.onCreate 中创建的单例。在工厂中可以配置缓存限制、HTTP 客户端、自定义解码器和日志记录。默认情况下,Coil 使用带有现成连接池的 OkHttp。
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()
}
}
常见问题
Coil — 用于在 Android 上加载图片的库,用 Kotlin 编写并使用协程。用于从网络、资源或文件系统异步加载、缓存和显示位图图像。
Coil 100% 用 Kotlin 编写,使用 协程 代替 Glide 中的回调机制。Coil 具有更小的 APK 大小(~150 KB vs ~500 KB)和通过 AsyncImage 提供的 Jetpack Compose 原生支持。
在 build.gradle.kts 中添加依赖 io.coil-kt:coil:2.6.0。对于 Jetpack Compose,还要添加 io.coil-kt:coil-compose:2.6.0。库会自动通过 ContentProvider 注册 ImageLoader。
Coil 支持 JPEG、PNG、WebP、BMP、SVG(通过 coil-svg 模块)和 GIF(通过 coil-gif 模块)。AVIF 和 HEIF 格式通过自定义解码器在 Android 10+ 设备上得到支持。
缓存通过 ImageLoader.Builder 配置:memoryCache 指定堆的百分比,diskCache 指定路径和以字节为单位的限制。缓存策略(ENABLED、DISABLED、READ_ONLY)通过 CachePolicy 按请求配置。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。