一、为何优化
- 安装包体积越小,则用户抗拒下载心理越弱,可提高确认下载率;
- 小安装包让用户下载时更主动允许流量下载,而无需延迟到Wifi;
- 从用户体验层面来说,小体积安装包减少下载时间,同时减少安装耗时;
- 安装包大小与安装后占用存储空间成正相关;
- 推广按照流量收费,安装包体积越小,推广成本越低;
- 如果交付产物是SDK,甚至会影响宿主安装包体积;
二、原因
- 手抖把同一张图片同时放入 xhdpi 和 xxhdpi (同事犯过这错);
- 资源图片没有压缩;不需要透明通道的 png 没有保存为 jpg;
- 存放图片实际尺寸过大;
- 相同字符串使用不同资源名称造成重复声明;
- 废弃、冗余代码没有及时清理,依然被其他代码引用令 Proguard 无法自动裁剪;
三、基础方案
- 用 Android Lint 检查开发资源是否重复;
- 构建安装包时用脚本压缩图片资源;
- 把所有 jpg 和 png 图片资源转换成 webp 或矢量资源;
- 启用 android.enableR8.fullMode=true;
- 启用 shrinkResources true 和 zipAlignEnabled true;
- 使用腾讯 AndResGuard 工具压缩安装包的资源名称;
四、主流格式
4.1 PNG
是否需要使用 PNG 取决于应用场景的图片是否需要透明。透明通道的图片需使用 PNG (或 WebP),这种情况只能对图片进行有损压缩减少体积。如果图片不需要透明通道但使用了 PNG 格式,应先把 PNG 转换为 JPG,再对 JPG 进行有损压缩。
4.2 JPEG
对 JPG 直接进行压缩或考虑利用 WebP 算法优势进一步减少体积。
4.3 WebP
需要注意 Android 对 WebP 支持有版本限制,和 PNG 一样支持透明通道。
WebP的有损压缩算法是基于VP8视频格式的帧内编码[17],并以RIFF作为容器格式。[2] 因此,它是一个具有八位色彩深度和以1:2的比例进行色度子采样的亮度-色度模型(YCbCr 4:2:0)的基于块的转换方案。[18] 不含内容的情况下,RIFF容器要求只需20字节的开销,依然能保存额外的 元数据(metadata)。[2] WebP图像的边长限制为16383像素。[5]
五、其他方案
- 使用原生代码代替文件配置,如这些文件:drawable、anim、string、color、layout;
- 图片资源存放在云端,运行时通过图片框架加载;
- 手写组件、Anko、Android JetPack 等代码实现UI布局;
- 手动压缩 raw、assets 文件夹的图片、音频、JS文件,这些文件构建时默认不会压缩;
- 减少类声明、内部类、抽象接口,或更进一步说就是减少代码量;
- 使用频率低的功能在使用时下载插件再启动;
- 原生界面和 WebView 混合开发,功能转移到线上;
- 用自定义 View 在 Canvas 绘制替换矢量视图;
对 Android 来说,还有比较极端的技术方案:只留下一个尺寸的图片资源,例如:drawable-xxxhdpi。只保留一个尺寸可有效节省空间,但低端手机需要读取更大的图片资源,经过缩放才到合适的展示尺寸。对于需要出海的应用来说,可以按照屏幕尺寸个性化派发安装包。
印象里国内有不少大厂都用这种方法,我个人也推荐这种方法。为了避免每次读取文件都用原图,要用 Glide 等图片加载框架的磁盘策略,缓存缩放后的资源。
六、效果
对工程图片进行压缩、部分非重要图标只保留一份资源的处理,结果如下:
后期进一步把几张 PNG 海报转换为 JPG,体积最终减少到19.6MB,累计压缩20%体积。