App包体积优化调研
1.背景
随着业务的增加,工程中引入越来越多的业务代码和第三方库, 安装包体积越来越大。
App 的包大小做优化的目的一方面为用户手机节省更多的空间,同时也节省用户流量,提高用户的下载速度。
App包体积过大,对用户更新升级率也会有很大影响。
App Store 官方规定 App 安装包如果超过 200MB,只可以在 WiFi 环境下载(iOS13已取消限制),以避免用户超出运营商套餐流量。
2.App包比重分析
2.1 ipa安装包构成
.ipa(iOS App Store Package):是一个压缩包,iOS 应用程序归档文件,即提交到 App Store Connect 的文件。
.app(Application) :是应用的具体描述,即安装到 iOS 设备上的文件。
当我们拿到 Archive 后的 .ipa,使用 “归档实用工具” 打开后,Payload 目录下放的就是 .app 文件。
至于包体积,评判的标准,应以 App Store 上看到的为准。
2.3 结果分析
图片资源占138M
FrameWorks165.166M
Unix可执行文件120.5M
这三部分占比较重,因此对以下三部分着重进行了以下分析。
2.3.1 FrameWorks
FrameWorks由framework项目中引入依赖库、dylib(系统库)两部分组成。
2.3.2 图片资源
在.app第一层级目录下存在大量的图片资源,其中有几张单张大小达到500KB左右
经过排查,定位发现有些图片来自于pod库,pod库在编写podspec的时候,用了以下的语句指定资源文件,导致打包的时候png被直接拷贝到安装包中。
Assets.car文件是由Assets.xcassets生成的压缩文件。
通过工具解压后分析文件夹中图片,发现存在体积较大图片,及重复图片。
2.3.3 可执行文件
LinkMap文件是Xcode产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分。
通过设置Project->Build Settings->Write Link Map File为YES, File,build完后就可以在相应的路径看到LinkMap文件,通过LinkMap分析工具进行分析,得出项目中占用可执行文件较大的模块。(此处列举了300K以上部分)
3.解决方案
3.1 资源瘦身
3.1.1 删除无用图片资源、重复图片资源
排查未使用图片资源
通过LSUnusedResources对项目中图片资源进行快速排查
注意:这个app的原理是,对某一文件目录下所有的源代码文件进行扫描,用正则表达式匹配出所有的@”xxx”字符串(会根据不同类型的源代码文件使用不同的匹配规则),形成“使用到的图片的集合”,然后扫描所有的图片文件名,查看哪些图片文件名不在“使用到的图片的集合”中,然后将这些图片文件呈现在结果中。
所以可能会有误伤,需要开发人员对未使用资源进行再次确认方可手动删除。
大图片处理
针对图片体积较大的问题,可由设计提供体积较小的切图,或者使用工具压缩后再放置项目中。
以下为较常用压缩工具:
同时建议将主工程中散落的图片资源,比如DoAsset文件夹下图片资源,使用Apple推荐的.xcassets来管理,它会把里边的所有png格式的图片压缩成一个Assets.car文件,压缩比率比其他方式管理图片要高。
将图片放置到云端 On-Demand Resources
苹果从iOS 9开始引入了On Demand Resource功能,即将部分图片可以被放置在苹果的服务器上,不随着app的下载而下载,系统会根据App运行情况,动态下载并加载所需资源,而在存在存储空间不足时,自动删除这类资源。也是一个优化安装包大小的方法。
不过On-Demand Resources一般在游戏关卡中使用较多,而且受到网络的影响需要下载并加载所需资源,在图片资源体量不太大的情况下,不太建议使用。
图片资源重复利用
在基础组件库中,已存放了部分统一的的图标资源,可以在基础库中对使用率较高的图标进行统一存放。在减少App体积的同时,也方便后期对图标的更新与维护。
图片管理方式
在调研中发现,目前项目中各个模块管理图片资源的方式不尽相同
主工程有以下两种方式:
- png图片直接放在主工程目录下
- xcassets管理
pod库有以下两种方式:
- 图片放置bundle中,通过bundle访问图片资源
xcassets管理
从App瘦身的角度而言,xcassets 里的 2x 和 3x,会根据具体设备分发,不会同时包含。而 Bundle 会都包含。因此应尽量通过Asset Catalog来管理图片。
另外在Cocoapods的官方文档中提到了两点:
使用resource_bundles能大大减小命名冲突的概率
使用resources资源文件是直接拷贝到app中的,没有经过Xcode的优化
3.2 可执行文件优化
3.2.1. 排查未使用类及方法
项目中Dead Code Stripping此选项为Yes, C/C++/Swift 等静态语言编译器会在 link 的时候移除未使用的代码,但是对于OC等动态语言是无效的。
因此只需监测OC无效代码即可(在项目启动优化时已进行排查)
3.2.2 去除功能重复三方库
对项目中功能类似的三方库进行排查,是否可进行精简(项目启动优化已进行)。
3.3 编译选项优化
iOS 9之后苹果官方提供了App Thinning:Slicing、Bitcode,On Demand Resouces;
3.3.1 BitCode
什么是BitCode
Bitcode官方文档解释 是Xcode编译打包程序的一种中间码,在包含Bitcode配置的程序被上传到App Store之后,App Store也可以对其进行编译和链接。同时,Bitcode允许苹果后期重新优化程序的二进制执行文件。苹果会根据下载应用的用户手机指令集类型生成该指令集的二进制文件,进行下发下载 ,如下
而在 BitCode 之前没我们都是把所有需要的 CPU 架构集合打包成一个 Fat Binary,结果就是用户最终下载的安装包之中有很多冗余的CPU 架构支持代码。
如何设置
在Build Settings下,将Enable Bitcode设置为Yes来完成。(项目中所有的库都必须支持bitcode)
需要注意的问题
工程开启BitCode要求我们依赖的静态库和动态库都是含有 BitCode 的,不然就会打包失败。集成的其他第三方库也需要支持Bitcode(项目目前不支持/(ㄒoㄒ)/~~)。
开启 BitCode 之后,由于最终的可执行文件是 Apple 自动生成的,同时产生新的符号表文件,所以我们使用原本打包生成的 dSYM 符号化文件是无法完成符号化的。
所以我们需要在上传至 App Store 时需要勾选 Include app symbols for your application to receive symboilcated crash logs from Apple:勾选之后 Apple 会给我们生成 dSYM,然后在 Xcode -> Organizer 或者 iTunes Connect 中下载对应的 dSYM 来进行符号化。
3.3.2 编译选项配置
在App体积优化上,可配置的选项:
编译选项 | 当前状态 | 用途 | 备注 |
---|---|---|---|
Optimization Level | Fastest,Smallest | 编译器执行所有优化,不会增加代码的长度,执行文件占用更少内存 | 已配置 |
Strip Linked Product | YES | 去除符号信息 | 已配置 |
Dead Code Stripping | YES | C/C++/Swift 等静态语言编译器会在 link 的时候移除未使用的代码 | 已配置 |
Deployment PostProcessing | NO | Deployment的总开关 | 可配置为Yes |
Symbols hidden by default | YES | 去除符号信息 | 已配置 |
Compress PNG Files | YES | 打包的时候对图片进行无损压缩 | 已配置 |
Remove Text Medadata From PNG Files | YES | 移除 PNG 资源的文本字符,比如图像名称、创作时间、注释等信息 | 已配置 |
Deployment Postprocessing 是Deployment的总开关,所以在打开这个选项之前 Strip Linked Product是不起作用的。
打开Deployment Postprocessing对包体积进行了对比,效果还是较明显的:
Deployment Postprocessing | ipa大小(MB) | .app大小(MB) |
---|---|---|
开启前 | 249.6 | 443.5 |
开启后 | 243.3 | 405.2 |
4 监控机制
4.1 监控
在对包体积做减法的同时,也要严控新版本功能迭代体积的增加。因此,建立预警机制,也显得尤为重要。
在做调研的过程中,发现今日头条的包体积监控机制提供了一个很好的思路:
4.2 流程规范
监控机制只是为了发现问题,更重要的是我们在开发的过程中,养成良好的开发习惯,避免造成这些问题。
- 在实现新功能时,首先考虑通用组件是否已提供此功能的实现,可否复用
- 在引入新的三方库时要慎重,是否会造成包体积的显著增加,考虑是否可以抽离,而不是引入该库
- 对于新增加的图标等资源文件,要检查一下大小,若体积太大,压缩处理后再进行使用
- 废弃模块及早清理;