Skip to content

图片加载:Coil/Kamel

在 Kotlin Multiplatform 项目中,图片加载是常见需求。Coil 3.x 和 Kamel 是两个流行的跨平台图片加载库。

Coil 3.x (推荐)

源:Coil Documentation

Coil 3.0+ 提供完整的 KMP 支持,是 Android 平台 Coil 的跨平台版本。

依赖配置

toml
[versions]
coil = "3.0.4"

[libraries]
coil-core = { module = "io.coil-kt.coil3:coil", version.ref = "coil" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref = "coil" }
kotlin
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation(libs.coil.core)
            implementation(libs.coil.compose)
            implementation(libs.coil.network.ktor)
        }
    }
}

最新版本查询:https://github.com/coil-kt/coil/releases

Compose 中使用

kotlin
// commonMain
import coil3.compose.AsyncImage
import coil3.compose.LocalPlatformContext
import coil3.request.ImageRequest
import coil3.request.crossfade

@Composable
fun UserAvatar(url: String) {
    AsyncImage(
        model = ImageRequest.Builder(LocalPlatformContext.current)
            .data(url)
            .crossfade(true)
            .build(),
        contentDescription = "User avatar",
        modifier = Modifier.size(64.dp)
    )
}

// 带占位图和错误图
@Composable
fun ProductImage(url: String) {
    AsyncImage(
        model = url,
        contentDescription = "Product",
        placeholder = painterResource(Res.drawable.placeholder),
        error = painterResource(Res.drawable.error),
        modifier = Modifier.fillMaxWidth()
    )
}

自定义配置

kotlin
// commonMain
import coil3.ImageLoader
import coil3.PlatformContext
import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import coil3.request.crossfade
import co​il3.util.DebugLogger

fun createImageLoader(context: PlatformContext): ImageLoader {
    return ImageLoader.Builder(context)
        .crossfade(true)
        .memoryCache {
            MemoryCache.Builder()
                .maxSizePercent(context, 0.25)
                .build()
        }
        .diskCache {
            DiskCache.Builder()
                .directory(getCacheDir(context) / "image_cache")
                .maxSizeBytes(512 * 1024 * 1024) // 512 MB
                .build()
        }
        .logger(DebugLogger())
        .build()
}

Kamel (备选方案)

源:Kamel Documentation

Kamel 是专为 Compose Multiplatform 设计的图片加载库。

依赖配置

toml
[versions]
kamel = "1.0.0"

[libraries]
kamel-image = { module = "media.kamel:kamel-image", version.ref = "kamel" }

基础使用

kotlin
import io.kamel.image.KamelImage
import io.kamel.image.asyncPainterResource

@Composable
fun ImageFromUrl(url: String) {
    KamelImage(
        resource = asyncPainterResource(url),
        contentDescription = "Image",
        modifier = Modifier.size(200.dp),
        onLoading = {
            CircularProgressIndicator(progress = { it })
        },
        onFailure = { exception ->
            Text("Error: ${exception.message}")
        }
    )
}

缓存配置

kotlin
import io.kamel.core.config.KamelConfig
import io.kamel.core.config.takeFrom
import io.kamel.image.config.*

val customKamelConfig = KamelConfig {
    takeFrom(KamelConfig.Default)
    
    // 内存缓存
    imageBitmapCacheSize = 1000
    
    // 磁盘缓存
    fileFetcher()
}

// 使用自定义配置
CompositionLocalProvider(LocalKamelConfig provides customKamelConfig) {
    App()
}

平台特定实现

expect/actual for Context

kotlin
// commonMain
expect class ImageContext

expect fun getImageContext(): ImageContext

// androidMain
import android.content.Context

actual typealias ImageContext = Context

actual fun getImageContext(): ImageContext {
    return AppContext.application
}

// iosMain
actual class ImageContext

actual fun getImageContext(): ImageContext {
    return ImageContext()
}

图片类型处理

ByteArray 转 ImageBitmap

kotlin
// commonMain
import androidx.compose.ui.graphics.ImageBitmap

expect fun ByteArray.toImageBitmap(): ImageBitmap

// androidMain
import android.graphics.BitmapFactory
import androidx.compose.ui.graphics.asImageBitmap

actual fun ByteArray.toImageBitmap(): ImageBitmap {
    return BitmapFactory.decodeByteArray(this, 0, size).asImageBitmap()
}

// iosMain
import androidx.compose.ui.graphics.toComposeImageBitmap
import org.jetbrains.skia.Image

actual fun ByteArray.toImageBitmap(): ImageBitmap {
    return Image.makeFromEncoded(this).toComposeImageBitmap()
}

最佳实践

✅ 实践 1:预加载图片

kotlin
fun preloadImages(imageLoader: ImageLoader, urls: List<String>) {
    urls.forEach { url ->
        val request = ImageRequest.Builder(context)
            .data(url)
            .build()
        imageLoader.enqueue(request)
    }
}

✅ 实践 2:图片压缩

kotlin
val request = ImageRequest.Builder(context)
    .data(url)
    .size(800, 600)  // 限制尺寸
    .build()

✅ 实践 3:缓存策略

kotlin
// 强制刷新
ImageRequest.Builder(context)
    .data(url)
    .diskCachePolicy(CachePolicy.WRITE_ONLY)
    .memoryCachePolicy(CachePolicy.WRITE_ONLY)
    .build()

// 仅使用缓存
ImageRequest.Builder(context)
    .data(url)
    .diskCachePolicy(CachePolicy.READ_ONLY)
    .build()

选择 Coil 3.x 还是 Kamel 取决于项目需求。Coil 3.x 生态更成熟,Kamel 更轻量级。