作者:京东零售 何骁
介绍
京喜APP早期开发主要是快速原生化
迭代替代原有H5
,提高用户体验,在这期间也积累了不少性能问题。之后我们开始进行一些性能优化相关的工作,本文主要是介绍京喜图片库
相关优化策略以及关于图片相关的一些关联知识。
图片性能问题
作为电商APP,图片在各个业务场景被大量使用。我们需要做到尽可能降低网络消耗
/内存消耗
/硬盘消耗
,同时不降低图片质量
,提高图片加载速度
,给用户带来更好的使用体验。基于这些性能目标,我们也通过初步性能评估梳理出了一些性能问题:
图片加载慢/流量消耗高
图片链接主要由后台接口下发,下发图片格式
和尺寸
由每个业务后台指定。部分业务没有使用更小的图片格式比如WebP
,或图片尺寸
过大,都会使图片过大导致网络消耗高。特别是网络状况不佳的场景,图片加载过慢给用户带来不好的体验。同时也会导致更多的I/O读写
和解码
耗时,造成更多的电量消耗。
图片内存占用高
经过初步的APP内存使用评估,图片内存消耗占APP总内存消耗的比例最高
,特别是大尺寸图片会占用很多内存。一方面APP占用太高内存退到后台容易被系统杀死,导致下次打开重新启动影响体验。另一方面APP大量使用内存,容易被系统杀死产生OOM
。特别是我们目前有大量的低端设备用户,设备内存相对比较低。
优化方向
基于上面分析出的一些性能问题,我们对图片框架进行了整体重构优化。一方面是降低
图片网络传输,提高图片加载速度。另一方面是减少
图片内存消耗。
最小化网络传输
京东图片服务器
提供了多种处理功能,例如图片格式转换,图片降质,图片缩放,图片圆角
等功能。这些功能通过在图片URL
中添加特定参数实现,图片服务器会根据参数设置提前将图片处理完成并保存到CDN
服务器。我们可以通过添加图片处理参数,减少图片传输大小。
虽然后台可以提前进行URL预处理
,下发已添加过图片参数的图片URL
。但是由于对接后台业务很多,每个业务图片参数设置差异很大无法统一,而且可能会造成性能影响,例如没有使用webP
图片格式,下发太大的图片尺寸
。同时考虑到推动各业务后台修改成本也很高,并且前端机型多,不同机型需要使用不同的图片尺寸。另外也不方便灰度降级功能,后续功能修改也不方便。所以在客户端
进行图片URL预处理
是更好的方式,可以统一控制,也方便之后功能更新。
图片URL预处理
图片库在网络图片加载前,检测是否是京东
域名的图片URL
。如果域名
匹配,图片框架先对图片URL
进行预处理,预处理包括域名统一
,添加缩放参数
,添加webP参数
,添加降质参数
的方式减少图片网络传输大小。
提示:因为后台返回的图片
URL
可能会带有一部分图片处理参数,例如https://img11.360buyimg.com/img/pingou-head/25.jpg!webp
,直接追加图片参数可能会导致图片处理参数不生效,或格式错误导致加载失败。所以转换时会先将所有图片参数提前计算出来,之后一起处理,避免添加重复参数。
域名统一
目前图片服务器提供了多个图片域名可使用,例如m.360buyimg.com
,img10.360buyimg.com
等多个域名。m.360buyimg.com
主要提供给移动端
使用。但是由于对接了各种业务后台,导致接口会下发不同的域名图片。图片使用不同域名
可能会导致以下问题:
不利于缓存复用
- 图片框架通常默认以URL
字符串生成图片缓存key
,不同域名
导致生成不同的缓存key
。硬盘缓存
无法复用会导致图片重复下载,内存缓存
无法复用导致同样的图片占用多份内存。不利于HTTP/2连接复用
- 大部分界面图片比较多,很多场景都会同时加载多张图片,特别是首屏
通常会加载几十张图片。当加载多个图片时,每个域名都需要重新建立HTTPS
连接,经历DNS解析/TCP连接/TLS握手
过程(目前一次HTTPS请求创建过程大概耗时50-150ms
)。如果利用HTTP/2
链接复用就只需要创建一次HTTPS
请求,之后的图片请求可以减少这部分的耗时。
所以在预处理时,如果是京东
域名的图片,将图片URL域名
统一替换为m.360buyimg.com
。
追加图片参数
图片缩放
很多业务后台返回的原始图片URL
的size
都比客户端实际显示的size
要大。一方面导致使用更多的网络流量造成浪费。另一方面会导致占用更多内存。同时因为图片size
和实际显示size
不一致导致像素不对齐
,GPU
需要做额外的插值处理,也会一定的影响渲染性能。所以我们通过添加缩放参数的方式,指定图片服务器下发更小和更匹配实际显示size
的图片尺寸。
动态scale计算尺寸
因为iOS
设备主要使用2x/3x
的分辨率,所以业务方使用API时需要传入对应的ptsize
大小,图片库内部根据设备的scale
进行动态计算出真实的像素宽高。
提示:
android
设备因为屏幕差异比较大,更适合使用固定的scale
。太多的图片尺寸不利于CDN
缓存,无缓存的时候需要对图片进行相关参数处理,图片处理本身是耗时操作。
Scale降级
低端机降级
- 对于部分3x
scale的低端设备,因为机器本身内存比较低,使用3x
分辨率计算出来的图片像素
宽高比较大,会造成更多的内存消耗以及解码/渲染更多的性能消耗。所以对于宽高超过一定要求的图片,降级到使用2x
分辨率来计算像素
宽高,减少设备性能消耗。iPad降级
- 因为目前APP并没有针对iPad
做特定优化,所以iPad设备下默认是放大显示。这会导致在iPad
下图片尺寸计算出来特别大。所以也是针对iPad图片尺寸做了特定限制,防止下发图片尺寸过大。大图片降级
- 正常情况下图片宽/高
不应该超过屏幕宽/高
。为了防止部分业务使用过大的图片size
,所以添加了一个限制,最终生成的图片像素
尺寸不能超过屏幕宽/高
。
降质
图片服务器支持0-100
的图片质量参数设置,通过降低图片质量可以减少图片大小,但是质量降低太多也会影响图片的观看体验。我们将图片质量参数设置为q70
,指定图片服务器下发70%
质量的图片。对于大部分业务,一方面可以大幅减少图片下载大小,同时也可以保证观看体验。通过添加图片降质参数至少可以减少30-40%
的图片大小。
使用WebP
按照Google
官方的数据,与PNG
相比,WebP
无损图像的字节数要少26%
。WebP
有损图像比同类JPG
图像字节数少25-34%
。图片服务器支持转换webP
格式,可以减少图片大小。针对png
/jpg
图片格式,添加webP
参数,指定图片服务器下发webp
格式。虽然webP
相比png
/jpg
图片解码需要更长时间,但相对网络传输速度提升还是很大。
提示:由于目前图片服务器并不支持
GIF
转webP
,GIF并没有做处理。
URL预处理缓存
添加轻量缓存,提高URL
转换性能。因为URL
转换本身有一定的耗时,而且单个图片URL
可能会多次加载/多次转换。转换后的URL
会直接保存到缓存中,下次使用可以直接返回。缓存key
由URL
+相关图片转换参数
拼接组成。
图片API设计
图片处理参数通过options
设置,默认使用q70
图片质量以及webP
格式。业务方在调用加载图片方法时传入,下面是iOS
端的API:
imageView6.jx.setImage(url: URL(string: "https: