Coil图片加载库的使用介绍

Coil图片加载库的使用介绍

.

Coil官方介绍

Coil 是一个 Android 图片加载库,通过 Kotlin 协程的方式加载图片。Coil 名字的由来:取 Coroutine Image Loader 首字母得来。

.

特点如下:

  • 更快:

Coil 在性能上有很多优化,包括内存缓存和磁盘缓存,把缩略图存保存在内存中,循环利用 bitmap,自动暂停和取消图片网络请求等。

  • 更轻量级:

Coil 只有2000个方法(前提是你的 APP 里面集成了 OkHttpCoroutines),Coil 和 Picasso 的方法数差不多,相比 GlideFresco 要轻量很多。

  • 更容易使用:

CoilAPI 充分利用了 Kotlin 语言的新特性,简化和减少了很多样板代码。

  • 更流行:

Coil 首选 Kotlin 语言开发并且使用包含 Coroutines, OkHttp, OkioAndroidX Lifecycles 在内最流行的开源库。

.

Coil各个组件的作用

  • io.coil-kt:coil-base : 基础组件,提供了基本的图片请求、图片解码、图片缓存、Bitmap 复用等功能

  • io.coil-kt:coil : 默认组件,依赖于io.coil-kt:coil-base,提供了 Coil 类的单例对象以及 ImageView 相关的扩展函数

  • io.coil-kt:coil-gif : 包含两个 decoder 用于支持解码 GIFs

  • io.coil-kt:coil-svg : 包含一个 decoder 用于支持解码 SVG

  • io.coil-kt:coil-video : 包含两个 fetchers 用于支持读取和解码 任何 Android 的支持的视频格式 的视频帧

.

.

Coil的使用介绍

.

在项目build.gradle文件中添加依赖

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies {

	//核心库
	implementation("io.coil-kt:coil:1.2.2")

	//选择添加
	implementation("io.coil-kt:coil-gif:1.2.2")//支持GIF
	implementation("io.coil-kt:coil-svg:1.2.2")//支持SVG
	implementation("io.coil-kt:coil-video:1.2.2")//支持Video
}

.

.

Coil的基础使用

	//加载网络图片
	imageView.load("https://www.example.com/image.jpg")
	
	//加载本地资源图片
	imageView.load(R.drawable.image)
	
	//加载文件里的图片
	imageView.load(File("/path/to/image.jpg"))

使用可选的 `lambda` 块来添加配置项

	imageView.load("https://www.example.com/image.jpg") {
	    crossfade(true) //淡入淡出
		crossfade(1000)//设置显示动画的时间
	    placeholder(R.drawable.image) //占位图
		error(R.drawable.image2)//图片加载失败时显示的图
	}

.

Coil加载Gif图片

注: 需要添加 implementation("io.coil-kt:coil-gif:1.2.2") 依赖

val gifImageLoader = ImageLoader(this) {
            componentRegistry {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    add(ImageDecoderDecoder())
                } else {
                    add(GifDecoder())
                }
            }
}

image.load(url, gifImageLoader)

.

Coil加载SVG图

注: 需要添加 implementation("io.coil-kt:coil-svg:1.2.2") 依赖

val svgImageLoader = ImageLoader(this){
            componentRegistry {
                add(SvgDecoder(this@MainActivity))
            }
        }
 
image.load(R.drawable.image_svg, svgImageLoader)

.

Coil加载频帧

注: 需要添加 implementation("io.coil-kt:coil-video:1.2.2") 依赖

val imageLoader = ImageLoader.Builder(context)
.componentRegistry {
     add(VideoFrameFileFetcher())
     add(VideoFrameUriFetcher())
}
.build()

imageView.load(File("/path/to/video.mp4",imageLoader)) {
	videoFrameMillis(1000)
}

.

.

ImageLoader(图片加载管理器)

作用: 负责处理图片缓存、数据获取、图像解码、请求管理、Bitmap 缓存池、内存管理等工作

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


image.load(url, imageLoader)

补充:
为了达到性能最优,建议只创建一个 ImageLoader 并进行共享。主要原因是每个ImageLoader 都有自己的内存缓存和 Bitmap 缓存池

.

.

ImageRequest(图片加载请求)

作用:ImageLoader 加载图片提供所有的必要信息

ImageLoader有两种方法可以执行ImageRequest

区别:

  • enqueue(): 将ImageRequest要在后台线程上异步执行的队列排入队列。
  • execute():ImageRequest在当前协程中执行并返回一个ImageResult。
val request = ImageRequest.Builder(context)
    .data("https://www.example.com/image.jpg")//图片地址
	.crossfade(true)
   	.memoryCachePolicy(CachePolicy.ENABLED)//设置内存的缓存策略
    .diskCachePolicy(CachePolicy.ENABLED)//设置磁盘的缓存策略
    .networkCachePolicy(CachePolicy.ENABLED)//设置网络的缓存策略
    .target { drawable -> //图片加载之后的处理
        //处理逻辑
    }
    .build()

//创建请求后,将其传递给ImageLoader以使其入队并执行
val imageLoader = imageLoader.enqueue(request) 

image.load(imageLoader) 

.

.

execute()方法

作用: 可以调用 ImageLoaderexecute()方法来获取目标的Drawable图片

val request = ImageRequest.Builder(context)
    .data("https://www.example.com/image.jpg")
    .build()

val drawable = imageLoader.execute(request).drawable

.

.

预加载图片

.

1. 将图像预加载到内存中:

val request = ImageRequest.Builder(context)
    .data("https://www.example.com/image.jpg")
    //可选的,但设置ViewSizeResolver将通过限制图片应该预加载到内存的大小节省内存
    .size(ViewSizeResolver(imageView))
    .build()
imageLoader.enqueue(request)

2. 将网络图像仅预加载到磁盘缓存中,请禁用请求的内存缓存:

val request = ImageRequest.Builder(context)
    .data("https://www.example.com/image.jpg")
    .memoryCachePolicy(CachePolicy.DISABLED)
    .build()
imageLoader.enqueue(request)

.

.

Disposable(取消图片加载)

作用: 取消图片加载,是调用 load() 方法后的返回值

val disposable = imageView.load("https://www.example.com/image.jpg")

//取消正在进行的图片加载请求以及释放相关的资源
disposable.dispose()

//非阻塞式地等待任务结束
disposable.await()

.

.

Transformation(图片变换)

Coil默认提供了四种变换:

  • BlurTransformation() : 高斯模糊变换
  • CircleCropTransformation() : 圆形裁剪变换
  • GrayscaleTransformation() : 灰度变换
  • RoundedCornersTransformation() : 圆角变换
imageView.load("https://www.example.com/image.jpg") {

    transformations(
		CircleCropTransformation()//图片变换,将图片转为圆形 
	)
}

.

自定义图片变换

图片变换是基本所有的图片加载库都会支持的功能,Coil 对这个概念的抽象即 Transformation 接口,因此我们可以通过实现Transformation 接口来实现自定义图片变换

.

Transformation接口:

interface Transformation {

    /**
     * Return a unique key for this transformation.
     *
     * The key should contain any params that are part of this transformation (e.g. size, scale, color, radius, etc.).
     */
    fun key(): String

    /**
     * Apply the transformation to [input].
     *
     * @param pool A [BitmapPool] which can be used to request [Bitmap] instances.
     * @param input The input [Bitmap] to transform. Its config will always be [Bitmap.Config.ARGB_8888] or [Bitmap.Config.RGBA_F16].
     * @param size The size of the image request.
     */
    suspend fun transform(pool: BitmapPool, input: Bitmap, size: Size): Bitmap
}

注意

key()方法的返回值是用于计算图片在内存缓存中的唯一 Key 时的辅助参数,所以需要实现该方法,为 Transformation 生成一个可以唯一标识自身的字符串 Keytransform 方法包含了一个 BitmapPool 参数,我们在实现图形变换的时候往往是需要一个全新的 Bitmap,此时就应该通过 BitmapPool 来获取,尽量复用已有的 Bitmap

.

1、为图片添加水印

为图片添加水印的思路也很简单,只需要对 canvas 稍微坐下旋转,然后绘制文本即可

class WatermarkTransformation(private val watermark: String, @ColorInt private val textColor: Int,private val textSize: Float) : Transformation {

    override fun key(): String {
        return "${WatermarkTransformation::class.java.name}-${watermark}-${textColor}-${textSize}"
    }

    override suspend fun transform(pool: BitmapPool, input: Bitmap, size: Size): Bitmap {
        val width = input.width
        val height = input.height
        val config = input.config

        val output = pool.get(width, height, config)

        val canvas = Canvas(output)
        val paint = Paint()
        paint.isAntiAlias = true
        canvas.drawBitmap(input, 0f, 0f, paint)

        canvas.rotate(40f, width / 2f, height / 2f)

        paint.textSize = textSize
        paint.color = textColor

        val textWidth = paint.measureText(watermark)

        canvas.drawText(watermark, (width - textWidth) / 2f, height / 2f, paint)

        return output
    }

}

//使用自定义的变换
imageView.load(imageUrl) {
    transformations(
        WatermarkTransformation("萝莉", Color.parseColor("#8D3700B3"), 120f)
    )
}

.

2、为图片添加蒙层

Android 的 Paint 原生就支持为 Bitmap 添加一个蒙层,只需要使用其 colorFilter()方法即可

class ColorFilterTransformation(@ColorInt private val color: Int) : Transformation {

    override fun key(): String = "${ColorFilterTransformation::class.java.name}-$color"

    override suspend fun transform(pool: BitmapPool, input: Bitmap, size: Size): Bitmap {
        val width = input.width
        val height = input.height
        val config = input.config
        val output = pool.get(width, height, config)

        val canvas = Canvas(output)
        val paint = Paint()
        paint.isAntiAlias = true
        paint.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
        canvas.drawBitmap(input, 0f, 0f, paint)

        return output
    }
}


//使用自定义的变换
imageView.load(imageUrl) {
    transformations(
       ColorFilterTransformation(Color.parseColor("#9CF44336"))
    )
}

更多 Transformation 效果看这里:coil-transformations库

.

.

配置Coil的缓存策略

说明: Coil 的缓存机制是分为内存缓存,磁盘缓存 与 网络缓存,默认情况下,这三层缓存机制是全部启用的,即:全部可读可写

在请求图片的时候,我们可以在 lambda 块中配置本次请求的缓存策略

缓存策略有如下几种:

  • CachePolicy.ENABLED : 可读可写
  • CachePolicy.READ_ONLY : 只读
  • CachePolicy.WRITE_ONLY : 只写
  • CachePolicy.DISABLED : 不可读不可写,即禁用
imageView.load(imageUrl) {
    memoryCachePolicy(CachePolicy.ENABLED)//设置内存的缓存策略
    diskCachePolicy(CachePolicy.ENABLED)//设置磁盘的缓存策略
    networkCachePolicy(CachePolicy.ENABLED)//设置网络的缓存策略
}

.

默认情况下,每个 ImageLoader 都已设置为磁盘缓存,并将根据用户设备上的剩余空间设置 10-250MB 之间的最大缓存大小。但是,如果您设置了自定义OkHttpClient,则需要自己添加磁盘缓存,代码如下:

val imageLoader = ImageLoader.Builder(context)
    .okHttpClient {
        OkHttpClient.Builder()
            .cache(CoilUtils.createDefaultCache(context))
            .build()
    }
    .build()
    

imageView.load(imageUrl)

.

.

指定Coil全局的默认ImageLoader

说明: 如果我们想要设置应用内所有图片在加载时固定显示同一张 loading 图,在加载失败时固定显示一张 error 图, 那么就需要为 Coil 设定一个全局的默认配置。可以通过下面的方式来实现

.

方式一:

Coil.setImageLoader(
    ImageLoader.Builder(application)
        .placeholder(ActivityCompat.getDrawable(application, R.drawable.icon_loading)) //占位符
        .error(ActivityCompat.getDrawable(application, R.drawable.icon_error)) //错误图
        .memoryCachePolicy(CachePolicy.ENABLED) //开启内存缓存
        .callFactory(createOkHttp(application)) //主动构造 OkHttpClient 实例
        .build()
    )


private fun createOkHttp(application: Application): OkHttpClient {
    return OkHttpClient.Builder()
        .cache(createDefaultCache(application))
        .build()
}

private fun createDefaultCache(context: Context): Cache {
    val cacheDirectory = getDefaultCacheDirectory(context)
    return Cache(cacheDirectory, 10 * 1024 * 1024)
}

private fun getDefaultCacheDirectory(context: Context): File {
    return File(context.cacheDir, "image_cache").apply { mkdirs() }
}


val url = "https://notfound.png"
val imageView = findViewById<ImageView>(R.id.image_view)
val reloadButton = findViewById<Button>(R.id.reload_button)
reloadButton.setOnClickListener {
    imageView.load(url)
}

.

方式二:

class MyApplication : Application(), ImageLoaderFactory {

    override fun newImageLoader(): ImageLoader {
        return ImageLoader.Builder(applicationContext)
            .crossfade(true)
	        .placeholder(R.drawable.placeholder)
	        .error(R.drawable.error)
	        .availableMemoryPercentage(0.1)
	        .bitmapPoolPercentage(0.1)
            .build()
    }
}

.

全局的默认ImageLoader的使用

说明: ImageLoader 可以使用 Context.imageLoader 扩展函数检索单例

val imageLoader = context.imageLoader

.

.

Coil的扩展功能

请自行阅读文档这里就不做过多介绍了,因为用的机会不多。

.

.

从 Glide/Picasso 迁移

1. 基本用法

// Glide
Glide.with(context)
    .load(url)
    .into(imageView)

// Picasso
Picasso.get()
    .load(url)
    .into(imageView)

// Coil
imageView.load(url)

.

2. 通过设置图片ScaleType的方式:

imageView.scaleType = ImageView.ScaleType.FIT_CENTER
 
    // Glide
    Glide.with(context)
     .load(url)
     .placeholder(placeholder)
     .fitCenter()
     .into(imageView)
 
    // Picasso
    Picasso.get()
        .load(url)
        .placeholder(placeholder)
        .fit()
        .into(imageView)
 
    // Coil (autodetects the scale type)
    imageView.load(url) {
     placeholder(placeholder)
}

.

.

混淆规则

Coil 开箱即用,与 R8 完全兼容,不需要添加任何额外规则

如果你使用了 Proguard,你可能需要添加对应的混淆规则:CoroutinesOkHttp and Okio

.

Coroutines的混淆规则

# ServiceLoader support
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}

# Most of volatile fields are updated with AFU and should not be mangled
-keepclassmembers class kotlinx.coroutines.** {
    volatile <fields>;
}

# Same story for the standard library's SafeContinuation that also uses AtomicReferenceFieldUpdater
-keepclassmembers class kotlin.coroutines.SafeContinuation {
    volatile <fields>;
}

# These classes are only required by kotlinx.coroutines.debug.AgentPremain, which is only loaded when
# kotlinx-coroutines-core is used as a Java agent, so these are not needed in contexts where ProGuard is used.
-dontwarn java.lang.instrument.ClassFileTransformer
-dontwarn sun.misc.SignalHandler
-dontwarn java.lang.instrument.Instrumentation
-dontwarn sun.misc.Signal

.

OkHttp的混淆规则

# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform
-dontwarn org.conscrypt.ConscryptHostnameVerifier

.

Okio的混淆规则

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

.

.

参考资料

  • Coil的GitHub地址

  • Coil的API文档

  • Coil的源码分析文章

你可能感兴趣的:(android,java)