图片加载库原理
为什么要使用三级缓存
- 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了
- 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响
- 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知
- 所以提出三级缓存策略,通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量
什么是三级缓存
- 网络缓存, 不优先加载, 速度慢,浪费流量
- 本地缓存, 次优先加载, 速度快
- 内存缓存, 优先加载, 速度最快
三级缓存原理
- 首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
- 之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
- 总之,只在初次访问新内容时,才通过网络获取图片资源
图片加载库简介
通过对比来介绍三大图片加载库(由于ImageLoader已经停止更新,故不做介绍):
名称 | Picasso | Glide | Fresco |
---|---|---|---|
作者 | Square | Google员工开源项目 |
Picasso
功能和优点
- 支持统计监控功能,包括缓存命中率、已使用内存大小、节省的流量等。
- 支持优先级操作
- 支持延迟到图片尺寸计算完成加载
- 根据网络状态自动切换并发线程数量,包括飞行模式
主要缺点
- 没有本地缓存,Picasso的设计上是将缓存交给了Square的另一个库
OkHttp
,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。 - 内存占用量相对较大,甚至容易出现内存泄露,参见这个测试。
Glide
功能和优点
Glide总体设计图
- 支持多种媒体格式,包括Gif、WebP、缩略图,甚至Video。所以Glide实际上可以看作一个媒体缓存
- 支持优先级处理
- Glide的设计注重平滑的滑动,所以即使在列表中使用也非常流畅
- 支持图片的转码(
toBytes()
和transcode()
)和淡入淡出(crossFade
)效果 - 支持同步和异步加载
- 内置生命周期管理,支持Context(Activity、Fragment)调用,并提供了可供扩展的trimMemory接口。
- 支持网络连接配置,默认使用
UrlConnection
,也可以配合OkHttp
或者Volley
使用。 - 内存友好,包括自动清理内存缓存、缓存图片尺寸、默认使用
RGB_565
等。以至于Google官方把使用Glide作为App性能优化的典范,视频戳这里:Performance optimisations for android applications - Part 7 Using Glide image loading framework
主要缺点
- 相对于Square的Picasso和Facebook的Fresco,Glide虽然是由Google员工开发和维护并在Google产品中有一定的应用,但并不是Google的官方加载库。
Fresco
功能和优点
Fresco的设计和前面几个库都大不相同:它自己定义了一个显示图片的View(称为Drawee
)。大部分功能都可以使用SimpleDraweeView
来实现,并且几乎所有可配置的属性都支持XML配置。
- 三级缓存(内存2级,文件1级)
- 支持渐进式JPEG图片
- 支持Gif和WebP格式
- 支持圆角、Overlay、placeHolder、failure等多种场景显示。
- 支持图片预处理(Postprocessor)、缩放和旋转
- 对于5.0版本以下的设备,Fresco有专门的native内存管理优化
- 支持完全自定义的网络层
主要缺点
SimpleDraweeView
继承自ImageView
,但是如果应用程序调用了原生ImageView
的方法,例如setImageBackground
,会使得内部的DraweeHierachy完全丢失。而且在Fresco的中文文档中明确说明后续版本这个控件可能直接从View
类派生。所以在使用DraweeView时不能使用ImageView的任何API,也不能依赖于其继承自ImageView甚至View的继承树。不支持wrap_content属性。
SimpleDraweeView
必须显式指定宽度和高度。与
ScrollView
不兼容。使用RecyclerView
,ListView
,或GridView
没问题,但是使用ScrollView
会使得Fresco的内存管理失效,极大增加OOM的风险。其他Fresco使用陷阱可以看这里:Fresco-cn.org据说大列表中滑动会造成UI卡顿……
警察叔叔,是他说的类库依赖过于庞大。超过14K的方法和2M+的jar包依赖,使得Fresco在需要避免65k方法数的场景变得非常危险。
在0.10.0版本中,官方已经在尝试解决这个问题,确实有所缓解,但远没到解决这个问题的程度。
切换图片加载库会很困难。由于大部分功能依赖于
SimpleDraweeView
,因此和使用其他的加载库的布局文件会有所区别,导致老项目难以迁移;同时一开始就使用Fresco的新项目,以后无论出于什么原因需要换库,同样会很麻烦。
Glide和Picasso的对比
基础
Glide 和 Picasso 非常相似,Glide 加载图片的方式和 Picasso 如出一辙。
虽然两者看起来一样,但 Glide 更易用,因为 Glide 的 with 方法不光接受 Context,还接受 Activity 和 Fragment,Context 会自动的从他们获取,同时将 Activity/Fragment 作为 with()参数的好处是:图片加载会和 Activity/Fragment 的生命周期保持一致,比如 Paused 状态在暂停加载,在 Resumed 的时候又自动重新加载。
图像和内存
类型 | Glide | Picasso |
---|---|---|
默认格式 | RGB-565 | ARGB-8888 |
内存开销 | 小 | 大 |
图片细节 | 迅速 | 平滑 |
磁盘缓存 | 与ImageView尺寸大小一致,调整尺寸后需要重新加载,可代码配置为缓存全尺寸 | 全尺寸 |
特性 | 媒体缓存 | 仅支持图片缓存 |
结论
- 如果预见项目中会有很多圆角或渐进式JPEG等需求,可以使用Fresco;
- 如果是老项目已经使用了UIL或者Picasso,且依赖较多不容易修改,则可以继续使用;
- 如果还在纠结如何避免65K方法数,推荐使用Glide代替Fresco
因为Glide比后者实在轻巧了太多; - 如果是老项目需要换加载库,推荐使用Glide而不是Fresco, 降低迁移工作量;
- 如果是新项目,不推荐使用已经停止维护的UIL,也不推荐Picasso,推荐使用Glide;
- 如果你是Google粉……不用推荐了,Glide赶紧用!