Android 圖片加載庫之Coil的詳解與使用
一、介紹
在Android,資源的呈現(xiàn)主要有三大形式:文字、圖片、視頻。圖片有分為本地資源和網(wǎng)絡(luò)資源。
網(wǎng)絡(luò)資源需要通過下載然后綁定到ImageView中。
在前期我們使用的圖片加載框架如:picasso、gliade、imageload。這些框架都可以滿足我們正常的圖片加載到顯示,且這些框架都是通過Java語言開發(fā)出來。接下來,我將介紹一款以kotlin語言開發(fā)的框架Coil。
Coil的介紹
一、介紹
是一個(gè) Android 圖片加載庫,通過Kotlin協(xié)程的方式加載圖片
優(yōu)勢
- 更快:
Coil 在性能上有很多優(yōu)化,包括內(nèi)存緩存和磁盤緩存,把縮略圖存保存在內(nèi)存中,循環(huán)利用 bitmap,自動暫停和取消圖片網(wǎng)絡(luò)請求等。 - 更輕量級:
Coil 的包體很小,很多API依賴項(xiàng)目中的其他模塊的框架(如:前提是你的 APP 里面集成了 OkHttp 和 Coroutines),Coil 和 Picasso 的方法數(shù)差不多,相比 Glide 和 Fresco 要輕量很多,kotlin的語法支持,代碼更簡潔。 - 更容易使用:
Coil 的 API 充分利用了 Kotlin 語言的新特性,簡化和減少了很多樣板代碼,采用了類方法擴(kuò)展,常用的API已通過ImageViews擴(kuò)展到了ImageView中,可以直接使用 - 更流行更新:
Coil 首選 Kotlin 語言開發(fā)并且使用包含 Coroutines, OkHttp, Okio 和 AndroidX Lifecycles 在內(nèi)最流行的開源庫,很好的兼容最新Jetpack組件。
2、集成
io.coil-kt:coil-base : 基礎(chǔ)組件,提供了基本的圖片請求、圖片解碼、圖片緩存等
io.coil-kt:coil : 默認(rèn)組件,依賴于io.coil-kt:coil-base,提供了 Coil 類的單例對象以及 ImageViews 相關(guān)的擴(kuò)展函數(shù)
io.coil-kt:coil-gif : 用于支持解碼 GIFs
io.coil-kt:coil-svg : 用于支持解碼 SVG
io.coil-kt:coil-video : 包含兩個(gè) fetchers 用于支持讀取和解碼 任何 Android 的支持的視頻格式 的視頻幀
//依賴庫
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
3、使用
由于在kotlin支持方法的擴(kuò)展,所以ImageViews已支持提供了load方法,默認(rèn)只需要傳入圖片地址即可。
@JvmSynthetic
inline fun ImageView.load(
uri: String?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
url: HttpUrl?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(url, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
uri: Uri?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
file: File?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(file, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
@DrawableRes drawableResId: Int,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(drawableResId, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
drawable: Drawable?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(drawable, imageLoader, builder)
/** @see ImageView.loadAny */
@JvmSynthetic
inline fun ImageView.load(
bitmap: Bitmap?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(bitmap, imageLoader, builder)
load支持的請求類型
uri: String:圖片地址
url: HttpUrl:封裝好的請求url
uri: Uri:Uri的資源封裝
file: File:文件類型
drawableResId: Int:資源id
drawable: Drawable:drawable對象
bitmap: Bitmap:bitmap對象

加載load,返回一個(gè)Disposable,Disposable是IPC的接口,如下
interface Disposable {
/**
* Returns true if the request is complete or cancelling.
*/
val isDisposed: Boolean
/**
* Cancels any in progress work and frees any resources associated with this request. This method is idempotent.
*/
fun dispose()
/**
* Suspends until any in progress work completes.
*/
@ExperimentalCoilApi
suspend fun await()
}
通過Disposable可以判斷是否完成,主動調(diào)用以及等待。
擴(kuò)展:
Coil不僅提供了默認(rèn)的加載,還可以擴(kuò)展其他。
inline fun ImageView.load(
uri: String?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
通過源碼可以看到,requestbuild是支持?jǐn)U展的。默認(rèn)后面是{},可以在{}體中調(diào)用build中方法。
bind.image.load(url){
//build 擴(kuò)展體
crossfade(true) //漸進(jìn)漸出
placeholder(R.mipmap.ic_launcher) //加載中占位圖
error(R.mipmap.ic_launcher) //加載失敗占位圖
allowHardware(true)//硬件加速
allowRgb565(true)//支持565格式
lifecycle(lifecycle)//生命周期關(guān)聯(lián)
var default= DefaultRequestOptions()
defaults(default)
}
包括網(wǎng)絡(luò)緩存等,我們可以自己根據(jù)業(yè)務(wù)配置。如果你需要可以自定義一個(gè)ImageRequest.Build
ImageView高階變換
Transformation(圖片變換)
- BlurTransformation() : 高斯模糊變換
- CircleCropTransformation() : 圓形裁剪變換
- GrayscaleTransformation() : 灰度變換
- RoundedCornersTransformation() : 圓角變換

高階圖片變形通過transformations()來完成,支持多樣式。

BlurTransformation() : 高斯模糊變換
class BlurTransformation @JvmOverloads constructor(
private val context: Context,
private val radius: Float = DEFAULT_RADIUS,
private val sampling: Float = DEFAULT_SAMPLING
)
init {
require(radius in 0.0..25.0) { "radius must be in [0, 25]." }
require(sampling > 0) { "sampling must be > 0." }
}
1.radius:模糊半徑范圍在0到25之間,數(shù)值越大,模糊越深
2.sampling:采樣率必須大于,這個(gè)值是進(jìn)行縮放圖片大小的,(0,1),圖片是越來越大,[1,++oo),圖片是越來越小

GrayscaleTransformation() : 灰度變換
這個(gè)主要把彩色的圖片置灰,常見特殊忌日,可以設(shè)置,利用了ColorMatrixColorFilter對顏色進(jìn)行過濾。
RoundedCornersTransformation:圓角角度設(shè)置
class RoundedCornersTransformation( @Px private val topLeft: Float = 0f, @Px private val topRight: Float = 0f, @Px private val bottomLeft: Float = 0f, @Px private val bottomRight: Float = 0f )

A=topleft ,B=topright,C=bottomLeft,D=bottomRight。
如果只傳一個(gè)參數(shù),默認(rèn)就是四個(gè)角的角度,角度默認(rèn)值是0,任何角的坐標(biāo)都是(x,y),所以你設(shè)置角度無法只設(shè)置一個(gè)叫的一半。
如果想了解view的角度自定義,可以查看:
Android ImageView 四個(gè)角自定義角度,以及角度的變換_addroundrect_蝸牛、Z的博客-CSDN博客
參數(shù)擴(kuò)展
有人用習(xí)慣了Glide或者imageload,會發(fā)現(xiàn),可以通過配置來管理加載中,加載失敗等標(biāo)識,Coil同樣也支持,只是通過build提供的方法來完成。
inline fun ImageView.load(
uri: String?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
bind.load.setOnClickListener {
bind.image.load("https://img-blog.csdnimg.cn/3a4114c916904ecbadb7a71b77294eef.gif"){
crossfade(true) //漸進(jìn)漸出
placeholder(R.mipmap.ic_launcher) //加載中占位圖
error(R.mipmap.ic_launcher) //加載失敗占位圖
allowHardware(true)//硬件加速
// transformations(BlurTransformation(context,25f,2f))
//圓角
transformations(RoundedCornersTransformation(90f,0f,0f,0f))
}
}
有人會有疑問:
builder: ImageRequest.Builder.() -> Unit = {}
這個(gè)寫法,是返回builder類,通過指向類的內(nèi)部方法來完成,最后返回build。
小試牛刀:
class Build {
var msgs: String? = ""
fun setMsg(msg: String) = apply {
this.msgs = msg
}
}
class TestBody(var build: Build.() -> Unit = {}) {
fun log() {
var mBuild = Build()
mBuild.apply(build)
mBuild.msgs?.let { MyLog.log(it) }
}
}
fun main() {
var test = TestBody({
setMsg("hello")
})
test.log()
}
build是一個(gè)block object的,我們可以通過Build對象apply方法copy這個(gè)對象。
GIF動態(tài)圖片加載
Coil庫不僅支持靜態(tài)圖片,也支持動態(tài)圖片。只是動態(tài)和靜態(tài)的解析對象不同ComponentRegistry在注冊的時(shí)候,已提供了各種攔截與支持。
依賴庫的引入:
implementation("io.coil-kt:coil-gif:1.2.2")//支持GIF

decoder目前提供兩種方式:
api<28
/**
* A [Decoder] that uses [Movie] to decode GIFs.
*
* NOTE: Prefer using [ImageDecoderDecoder] on API 28 and above.
*/
class GifDecoder : Decoder
api>=28
/**
* A [Decoder] that uses [ImageDecoder] to decode GIFs, animated WebPs, and animated HEIFs.
*
* NOTE: Animated HEIF files are only supported on API 30 and above.
*/
@RequiresApi(28)
class ImageDecoderDecoder : Decoder
ImageLoad初始化:
注意GIFDecoder類的頂部注釋:
NOTE: Prefer using ImageDecoderDecoder on API 28 and above.
//創(chuàng)建 gif ImageLoader 實(shí)例
val imageLoader = ImageLoader.Builder(applicationContext)
.componentRegistry {
if (SDK_INT >= 28) {
add(ImageDecoderDecoder(applicationContext))
} else {
add(GifDecoder())
}
}.build()
//設(shè)置全局唯一實(shí)例
Coil.setImageLoader(imageLoader)
這樣我們就完成了Gif的ImageLoader的設(shè)置
如何監(jiān)聽圖片加載:
ImageRequest提供了fun listener(listener: Listener?) 方法。
bind.image.load("https://img-blog.csdnimg.cn/3a4114c916904ecbadb7a71b77294eef.gif") {
listener(object : ImageRequest.Listener {
override fun onCancel(request: ImageRequest) {
super.onCancel(request)
}
override fun onSuccess(request: ImageRequest, metadata: ImageResult.Metadata) {
super.onSuccess(request, metadata)
}
override fun onError(request: ImageRequest, throwable: Throwable) {
super.onError(request, throwable)
}
override fun onStart(request: ImageRequest) {
super.onStart(request)
}
})
}
關(guān)于配置
上面都是通過方法設(shè)置配置參數(shù),如何設(shè)置全局配置?需要我們自己去設(shè)置一個(gè)全局的ImageLoader.Builder
val okHttpClient = OkHttpClient.Builder()
.cache(CoilUtils.createDefaultCache(this))
.build()
val imageLoader = ImageLoader.Builder(this)
.availableMemoryPercentage(0.5f)
.diskCachePolicy(CachePolicy.ENABLED) //磁盤緩策略 ENABLED、READ_ONLY、WRITE_ONLY、DISABLED
.crossfade(true) //淡入淡出
.okHttpClient { //設(shè)置okhttpClient實(shí)例
okHttpClient
}.build()
Coil.setImageLoader(imageLoader)
api介紹:
1.availableMemoryPercentage:
設(shè)置用于此 ImageLoader 的內(nèi)存緩存和位圖池的可用內(nèi)存百分比,如果設(shè)置了百分百,那么cache將會失效
//源碼
fun availableMemoryPercentage(@FloatRange(from = 0.0, to = 1.0) percent: Double) = apply {
require(percent in 0.0..1.0) { "Percent must be in the range [0.0, 1.0]." }
this.availableMemoryPercentage = percent
this.memoryCache = null
}
2.memoryCachePolicy :
內(nèi)存緩存策略,有4中策略;
3.diskCachePolicy:
磁盤緩存策略,方式和內(nèi)存策略一致;
CachePolicy.ENABLED : 可讀可寫
CachePolicy.READ_ONLY : 只讀
CachePolicy.WRITE_ONLY : 只寫
CachePolicy.DISABLED : 不可讀不可寫,即禁用
短視頻首幀
Coil還有特別特別項(xiàng),支持短視頻首幀圖片加載。正常在項(xiàng)目中,如果該項(xiàng)目有短視頻業(yè)務(wù),在視頻不播放的狀態(tài)下,如何獲取首幀圖片?這個(gè)問題是很多公司思考的問題。
大公司在處理視頻首幀都是通過服務(wù)器跑的。這樣,在視頻集在列表展示只處理單張圖片即可。針對某一個(gè)視頻,特別是小公司的時(shí)候,Coil明顯很友好,可以在接收的范圍類,降低技術(shù)和服務(wù)器的成本。
依賴庫的引入:
implementation("io.coil-kt:coil-video:1.2.2")//支持Video
添加VideoDecoder
//創(chuàng)建 gif ImageLoader 實(shí)例
val imageLoader = ImageLoader.Builder(applicationContext)
.componentRegistry {
add(VideoFrameDecoder(context))
}.build()
//設(shè)置全局唯一實(shí)例
Coil.setImageLoader(imageLoader)
剩余的設(shè)置,都是通用設(shè)置,如果項(xiàng)目需要支持GIF、VideoFrame,可以同時(shí)支持進(jìn)去,都是通過componentRegistry add添加,可以追加進(jìn)去。
本文僅代表作者觀點(diǎn),版權(quán)歸原創(chuàng)者所有,如需轉(zhuǎn)載請?jiān)谖闹凶⒚鱽碓醇白髡呙帧?/p>
免責(zé)聲明:本文系轉(zhuǎn)載編輯文章,僅作分享之用。如分享內(nèi)容、圖片侵犯到您的版權(quán)或非授權(quán)發(fā)布,請及時(shí)與我們聯(lián)系進(jìn)行審核處理或刪除,您可以發(fā)送材料至郵箱:service@tojoy.com






