首页 / Android 入门教程 / 图片加载

Android 入门教程

图片加载

本教程共 100 篇 · 第 90 篇 · 更新于 2026-07-28 · 约 7 分钟阅读

AndroidAndroid 入门教程Coil图片加载Glide缓存

90. 图片加载

本节目标:学会用 Coil 在 Compose 里加载网络图片,掌握缓存策略、占位图、变换、列表优化,了解和 Glide/Picasso 的选型差异。

应用里几乎处处要加载图片:用户头像、商品图、新闻配图。直接用 BitmapFactory 解码会卡 UI、占内存、没缓存。专业的图片加载库帮你解决一切。

主流图片库对比

出品方Compose 原生现在用不用
CoilCoil 团队(Kotlin-first)是(AsyncImage推荐,新项目首选
GlideBumpTech通过 Accompanist主流,老项目广泛使用
PicassoSquare用得少了,新项目不推荐
FrescoFacebook重型,特殊场景才用

Coil 是 Kotlin-first 设计,API 简洁,Compose 原生支持,体积小。本教程主线。

Tip

Glide 老项目维护成本可控,不用强迁。新项目直接上 Coil,体验更好。

加依赖

dependencies {
    implementation("io.coil-kt:coil-compose:2.7.0")
}

网络权限(加载网络图需要):

<uses-permission android:name="android.permission.INTERNET" />

基本用法

Compose 里用 AsyncImage

AsyncImage(
    model = "https://example.com/avatar.jpg",
    contentDescription = "用户头像",
    modifier = Modifier.size(64.dp)
)

加载网络、本地文件、资源都支持:

AsyncImage(
    model = R.drawable.placeholder,
    contentDescription = null
)

AsyncImage(
    model = File("/sdcard/photo.jpg"),
    contentDescription = null
)

AsyncImage(
    model = Uri.parse("content://media/external/images/1"),
    contentDescription = null
)

占位图和错误图

加载中显示占位图,加载失败显示错误图:

AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data("https://example.com/avatar.jpg")
        .crossfade(true)  // 淡入动画
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .build(),
    contentDescription = "头像",
    modifier = Modifier.size(64.dp)
)

或者用更简洁的 SubcomposeAsyncImage,能用 Composable 当占位:

SubcomposeAsyncImage(
    model = "https://example.com/avatar.jpg",
    contentDescription = null,
    loading = { CircularProgressIndicator() },
    error = { Text("加载失败") }
)

变换

圆形、圆角、模糊等:

// 圆形
AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data("https://example.com/avatar.jpg")
        .transformations(CircleCropTransformation())
        .build(),
    contentDescription = "头像"
)

// 圆角
.transformations(RoundedCornersTransformation(16f))

// 模糊
.transformations(BlurTransformation(LocalContext.current, 25f))

Compose 里也能用 Modifier

AsyncImage(
    model = "https://...",
    contentDescription = null,
    modifier = Modifier
        .size(64.dp)
        .clip(CircleShape)
)

缓存

Coil 默认两级缓存:内存 + 磁盘。

  • 内存缓存:Bitmap 存在内存,最快。应用关了就没。
  • 磁盘缓存:图片文件存本地,下次启动还在。

默认策略:

  • 内存:存原始 Bitmap。
  • 磁盘:按 URL 哈希存,默认 250MB。

自定义缓存策略:

val imageLoader = ImageLoader.Builder(context)
    .memoryCache {
        MemoryCache.Builder(context)
            .maxSizePercent(0.25)  // 占可用内存 25%
            .build()
    }
    .diskCache {
        DiskCache.Builder()
            .directory(context.cacheDir.resolve("image_cache"))
            .maxSizeBytes(100L * 1024 * 1024)  // 100MB
            .build()
    }
    .build()

// 全局设置
Coil.setImageLoader(imageLoader)
Note

默认策略已经够用,普通项目不用自定义。需要精细控制(比如预加载某类图片)再调。

控制缓存行为

ImageRequest.Builder(context)
    .data(url)
    .memoryCachePolicy(CachePolicy.DISABLED)  // 不读内存缓存
    .diskCachePolicy(CachePolicy.ENABLED)
    .build()

策略:

  • ENABLED:用缓存。
  • DISABLED:不用缓存。
  • READ_ONLY:只读不写。
  • WRITE_ONLY:只写不读。

列表性能

长列表加载图片,要避免卡顿:

  1. 固定尺寸:给 AsyncImageModifier.size(...),不固定会触发测量多次。
  2. key():列表项有稳定 key,避免重组错位。
  3. 降采样:Coil 自动按 ImageView 大小降采样,不用管。
  4. crossfade(false):列表里关掉淡入,避免重复动画。
LazyColumn {
    items(users, key = { it.id }) { user ->
        AsyncImage(
            model = user.avatarUrl,
            contentDescription = user.name,
            modifier = Modifier.size(48.dp)
        )
    }
}

预加载

提前加载图片,用户滚动到时立即显示:

val imageLoader = LocalContext.current.imageLoader
val request = ImageRequest.Builder(context)
    .data(url)
    .size(128)
    .build()

LaunchedEffect(Unit) {
    imageLoader.enqueue(request)
}

加载状态监听

val painter = rememberAsyncImagePainter(
    model = ImageRequest.Builder(LocalContext.current)
        .data(url)
        .listener(
            onStart = { /* 开始加载 */ },
            onSuccess = { _, result -> /* 成功 */ },
            onError = { _, result -> /* 失败 */ }
        )
        .build()
)

Image(
    painter = painter,
    contentDescription = null
)

GIF 动图

implementation("io.coil-kt:coil-gif:2.7.0")

val imageLoader = ImageLoader.Builder(context)
    .components {
        if (SDK_INT >= 28) {
            add(ImageDecoderDecoder.Factory())
        } else {
            add(GifDecoder.Factory())
        }
    }
    .build()
AsyncImage(
    model = "https://example.com/animation.gif",
    contentDescription = null
)

SVG 矢量图

implementation("io.coil-kt:coil-svg:2.7.0")

val imageLoader = ImageLoader.Builder(context)
    .components {
        add(SvgDecoder.Factory())
    }
    .build()

AsyncImage(
    model = "https://example.com/icon.svg",
    contentDescription = null
)

常见坑

  1. contentDescription 忘写:无障碍读不出,写 null 表示装饰性图片。
  2. 不设尺寸:列表里尺寸不固定导致测量多次,卡顿。
  3. 加载本地大图不降采样:OOM。Coil 默认按目标尺寸降采样,但要给目标尺寸。
  4. 网络图加载失败没错误图:用户体验差。
  5. 同一 URL 多处加载重复请求:Coil 自动去重,不用担心。
  6. GIF 不加 gif 依赖:只显示第一帧。

小结

Coil 是 Kotlin-first 图片库,Compose 原生支持。AsyncImage 加载网络/本地/资源图,ImageRequest.Builder 配置占位图、变换、缓存策略。两级缓存默认够用,列表里固定尺寸 + key() 保性能。GIF 和 SVG 要加额外依赖。

下一章讲传感器,看怎么读加速度、陀螺仪等数据。