Coil - 基于Kotlin协程的Android图片加载库 - 3

376 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Image Loaders

ImageLoader服务对象, 用来执行ImageRequest. 它们处理缓存, 数据获取, 图片解码, 请求管理, Bitmap池技术, 内存管理, 等等. 新实例可以通过构建者进行创建和配置:

val imageLoader = ImageLoader.Builder(context)
    .crossfade(true)
    .build()

Coil在应用范围内创建单例ImageLoader并共享时, 性能最好. 这是因为每一个ImageLoader实例有它自己的内存缓存, 硬盘缓存和OkHttpClient实例.

缓存

每一个ImageLoader实例保留了最近解码的Bitmap的内存缓存, 也保留了从网络加载的图片的硬盘缓存. 两者可以在创建ImageLoader实例的时候进行配置:

val imageLoader = ImageLoader.Builder(context)
    .memoryCache {
        MemoryCache.Builder(context)
            .maxSizePercent(0.25)
            .build()
    }
    .diskCache {
        DiskCache.Builder(context)
            .directory(context.cacheDir.resolve("image_cache"))
            .maxSizePercent(0.02)
            .build()
    }
    .build()

在图片加载之后, ImageResult返回了key, 通过使用key可以访问图片的内存缓存和硬盘缓存. 而ImageResult是由ImageLoader.execute返回或者在ImageRequest.Listener.onSuccessImageRequest.Listener.onError里面.

注意

Coil 1.x依赖于OkHttp的存盘缓存. Coil 2.x拥有了自己的硬盘缓存, 并且不应该使用OkHttp的Cache.

单例 vs 依赖注入

默认的Coil构件(io.coil-kt:coil)包含单例ImageLoader, 它可以通过扩展函数context.imageLoader进行访问.

Coil在应用范围内创建单例ImageLoader并共享时, 性能最好. 这是因为每一个ImageLoader实例拥有自己的资源集.

单例ImageLoader可以在类Application内通过实现ImageLoaderFactory接口进行配置.

候选情况下, 你可以创建自己的ImageLoader实例并且通过像Dagger一样的依赖注入器将它们注入. 如果这样做的话, 一定要依赖io.coil-kt:coil-base库, 因为它不创建单例ImageLoader.

测试

ImageLoader是个接口, 可能通过假实现进行替换.

举个例子, 可以创建假的ImageLoader实现来一直同步地返回相同的Drawable:

class FakeImageLoader(private val context: Context) : ImageLoader {

    override val defaults = DefaultRequestOptions()
    override val components = ComponentRegistry()
    override val memoryCache: MemoryCache? get() = null
    override val diskCache: DiskCache? get() = null

    override fun enqueue(request: ImageRequest): Disposable {
        // Always call onStart before onSuccess.
        request.target?.onStart(request.placeholder)
        val result = ColorDrawable(Color.BLACK)
        request.target?.onSuccess(result)
        return object : Disposable {
            override val job = CompletableDeferred(newResult(request, result))
            override val isDisposed get() = true
            override fun dispose() {}
        }
    }

    override suspend fun execute(request: ImageRequest): ImageResult {
        return newResult(request, ColorDrawable(Color.BLACK))
    }

    private fun newResult(request: ImageRequest, drawable: Drawable): SuccessResult {
        return SuccessResult(
            drawable = drawable,
            request = request,
            dataSource = DataSource.MEMORY_CACHE
        )
    }

    override fun newBuilder() = throw UnsupportedOperationException()

    override fun shutdown() {}
}

这非常适合于需要一致渲染行为的屏幕截图和测试.