- 原标题: Coil vs Picasso vs Glide: Get Ready… Go!
- 原文地址: proandroiddev.com/coil-vs-pic…
- 原文作者:Miguel Ángel Ruiz López
- 这篇文章在 Medium 上到目前为止获得了 1.4K+ Star,非常好的一篇文章
Coil 作为图片加载库的新秀,和 Glide、Picasso 这些老牌图片库相比,它们的优缺点是什么以及 Coil 未来的展望?先来了解一下什么是 Coil。
Coil 是基于 Kotlin 开发的首个图片加载库,来自 Instacart 团队,来看看官网对它的最新的介绍。
通过这篇文章你将学习到以下内容,将在译者思考部分会给出相应的答案
接下来演示中表格中的数字,可能很难理解,但是为了更好地理解,在最后的部分,会以柱状图清晰的展示每个库的性能的对比,在译者思考部分会更深入的分析这三个图片加载库的性能,请耐心多读几遍,应该可以从中学到很多技巧。
Coil 作为图片库的新秀,越来越受欢迎了,但是为什么会引起这么多人的关注?在当今主流的图片加载库环境中 Coil 是一股清流,它是轻量级的,因为它使用了许多 Android 开发者已经在他们的项目中包含的其他库(协程、Okhttp)。
当我第一次看到这个库时,我认为这些都是很好的改进,但是我很想知道和其他主流的图片加载库相比,这个库能带来哪些好处,这篇文章的主要目的是分析一下 Coil 的性能,接下来我们来对比一下 Coil、 Glide 和 Picasso。
为了弄清楚这些库的性能如何,我们分别用它们实现了一个应用程序,这是一个简单的应用程序,它下载 10 张图片并以网格布局显示它们,如下图所示。
所有图片都是在同一时间可见的,几乎是在同一时间被请求的,因此,可以认为它们是并行加载。
另一个需要测试的重要点是第一次加载图片和从缓存中加载它们所花费的时间,已经进行了多次测试,覆盖了上面的场景。
我们开始第一个场景,当缓存为空时,从网络中下载图片。
Glide
在下面的表中,您可以看到当缓存为空时,从网络中下载图片所用的时间。注意,这些时间是测试 10 次之后的平均值。
下表展示了加载完整的图片列表所需的时间,以及平均时间。
Picasso
Picasso 的测试和 Glide 相同,当缓存为空时,从网络中下载图片,测试 10 次左右所用的时间的平均值。
以及加载完整的图片列表所需要的时间,以及平均时间。
Coil
现在来看一下今天主角 Coil ,和 Picasso、Glide 做相同的测试,当缓存为空时,从网络下载 10 次左右所用的时间的平均值。
以及加载完整的图片列表所需的时间,以及平均时间。
现在开始另外一个场景测试,当缓存不为空时,加载图片所需要的时间。
Glide
从缓存中加载图片所用时间,如下表所示。
从缓存中加载完整的图片列表所需的时间,以及平均时间。
Picasso
测试用例和 Glide 相同,从缓存中加载图片所用时间,如下表所示。
以及从缓存中加载完整的图片列表所需的时间,以及平均时间。
Coil
最后我们来看一下 Coil 如何呢,从缓存中加载图片所用时间,如下表所示。
同样的从缓存中加载完整的图片列表所需的时间,以及平均时间。
为了更好地理解我们在测试中得到的结果,我们可以从下面图表中看到这些数字,反应了从网络下载每个图片时的结果。
Glide 最快 Picasso 和 Coil 几乎相同。
但是当我们从缓存中加载的时候,正如你在下面的图片中看到的,在大多数情况下,Glide 最快,Coil 其次,Picasso 最慢。
另一个重要的测试加载完整的图片列表所花费的时间,这些数字非常重要,因为这是用户等待看到整个图片列表的时间。当图片从网络加载时,Glide 是最快的,其次是Picasso,Coil是最慢的。
从缓存加载的结果是不同的。Glide 和 Coil 几乎相同,Picasso 是最慢的。
从这些数字中我们可以得出几个结论:
作者从以下场景对 Coil、Glide、Picasso 做了全面的测试。
当缓存为空时,从网络中下载图片的平均时间。
从网络中下载图片所用的时间。 结果:Glide 最快 Picasso 和 Coil 几乎相同。
加载完整的图片列表所用的时间,以及平均时间。 结果:Glide 是最快的,其次是Picasso,Coil是最慢的。
当缓存不为空时,从缓存中加载图片的平均时间。
从缓存中加载图片所用的时间。 结果:Glide 最快,Coil 其次,Picasso 最慢。
加载完整的图片列表所用的时间,以及平均时间。 结果:Glide 和 Coil 几乎相同,Picasso 是最慢的。
图片加载库的选择是我们应用程序中最重要的部分之一,根据以上结果,如果你的应用程序中没有大量使用图片的时候,我认为使用 Coil 更好,原因有以下几点:
如果你的是图片类型的应用,应用程序中包含了大量的图片,图片加载的速度是整个应用的核心指标之一,那么现在还不适合使用 Coil。
Coil 涵盖了 Glide、Picasso 等等图片加载库所支持的功能,除此之外 Coil 还有一个功能 动态图片采样。
更多关于图片采样信息可以访问 Coil ,这里简单的说明一下,假设本地有一个 500x500 的图片,当从磁盘读取 500x500 的图片时,将使用 100x100 的映像作为占位符,等待加载完成之后才会完全显示,用官方的一张动图显示过程如下。
这种淡入动画效果,在视觉上体验非常舒适,占位符在主线程上设置,这样可以防止在 ImageView 为空的情况下出现白色闪烁,接下来让我们来看看如何使用 Coil?Coil、Glide 和 Picasso 使用上大比拼?
添加 Coil 依赖
implementation "io.coil-kt:coil:0.11.0" 复制代码
在 App moudule 下 build.gradle 文件中添加如下代码
kotlinOptions { jvmTarget = "1.8" } 复制代码
在项目中调用你需要的代码,这里汇总了 Coil 所有使用方式
// 将图片加载到 ImageView 中, 并开启图片采样 // 用到了 Kotlin 的高级特性扩展,调用更加简单 imageView.load("https://www.example.com/image.jpg"){ crossfade(true) } inline fun ImageView.load( uri: String?, imageLoader: ImageLoader = Coil.imageLoader(applicationContext), builder: LoadRequestBuilder.() -> Unit = {} ): RequestDisposable { return imageLoader.load(context, uri) { target(this@load) builder() } } // Coil 支持 Uri,File, String,HttpUrl, Bitmap, Drawable, DrawableId imageView.load(R.drawable.ic_launcher_background) imageView.load(File("/path/to/image.jpg")) imageView.load("content://com.android.externalstorage/image.jpg") // ...... // Coil 提供了四种转换: 模糊,圆形剪裁,灰度和圆角 imageView.load("https://www.example.com/image.jpg") { crossfade(true) placeholder(R.drawable.ic_launcher_background) transformations(CircleCropTransformation()) } // Coil 是一个 object 单例 val imageLoader1 = Coil.imageLoader(applicationContext) // 可以创建 或者 调用第三方库(Koin) 注入自己的实例。 val imageLoader2 = ImageLoader(applicationContext) // 在某些情况下,需要远程下载然后进行回调 var request = LoadRequest.Builder(applicationContext) .data("https://www.example.com/image.jpg") .target { drawable -> // Handle the successful result. } .build() imageLoader2.execute(request) 复制代码
Coil 基于 Kotlin 而设计,自然也拥有了 Kotlin 的高级函数的特性,使用比 Glide 和 Picasso 要简单很多。
基本使用
// Coil - 用到了 Kotlin 的高级特性扩展,一行代码加载图片 imageView.load(url) // Glide Glide.with(context) .load(url) .into(imageView) // Picasso Picasso.get() .load(url) .into(imageView) 复制代码
后台线程
// Coil:无阻塞和线程安全 val imageLoader = Coil.imageLoader(context) val request = GetRequest.Builder(context) .data(url) .size(width, height) .build() val drawable = imageLoader.execute(request).drawable // Glide:阻塞当前线程,一定不能从主线程调用 val drawable = Glide.with(context) .load(url) .submit(width, height) .get() // Picasso:阻塞当前线程,一定不能从主线程调用 val drawable = Picasso.get() .load(url) .resize(width, height) .get() 复制代码
自动检测 scaleType
imageView.scaleType = ImageView.ScaleType.FIT_CENTER // Coil:自动检测 scaleType imageView.load(url) { placeholder(placeholder) } // Glide Glide.with(context) .load(url) .placeholder(placeholder) .fitCenter() .into(imageView) // Picasso Picasso.get() .load(url) .placeholder(placeholder) .fit() .into(imageView) 复制代码
如何实现一行代码交换两个变量?我们先来回顾一下 JAVA 的做法
int a = 1; int b = 2; // JAVA - 中间变量 int temp = a; a = b; b = temp; System.out.println("a = "+a +" b = "+b); // a = 2 b = 1 // JAVA - 加减运算 a = a + b; b = a - b; a = a - b; System.out.println("a = " + a + " b = " + b); // a = 2 b = 1 // JAVA - 位运算 a = a ^ b; b = a ^ b; a = a ^ b; System.out.println("a = " + a + " b = " + b); // a = 2 b = 1 // Kotlin a = b.also { b = a } println("a = ${a} b = ${b}") // a = 2 b = 1 复制代码
致力于分享一系列 Android 系统源码、逆向分析、算法、翻译相关的文章,目前正在翻译一系列欧美精选文章,请持续关注,除了翻译还有对每篇欧美文章思考,如果对你有帮助,请帮我点个赞,感谢!!!期待与你一起成长。
由于 LeetCode 的题库庞大,每个分类都能筛选出数百道题,由于每个人的精力有限,不可能刷完所有题目,因此我按照经典类型题目去分类、和题目的难易程度去排序
每道题目都会用 Java 和 kotlin 去实现,并且每道题目都有解题思路,如果你同我一样喜欢算法、LeetCode,可以关注我 GitHub 上的 LeetCode 题解:Leetcode-Solutions-with-Java-And-Kotlin,一起来学习,期待与你一起成长
正在写一系列的 Android 10 源码分析的文章,了解系统源码,不仅有助于分析问题,在面试过程中,对我们也是非常有帮助的,如果你同我一样喜欢研究 Android 源码,可以关注我 GitHub 上的 Android10-Source-Analysis,文章都会同步到这个仓库