手机APP安装包缩减方案
安装包大小对于产品很重要
主要有如下几个原因:
1、手机APP安装包的大小会影响用户是否愿意花费流量来下载此APP;
2、包体越大下载过程越长,用户取消下载的可能性越大;
3、在手机空间不足,用户需要清理手机空间时,包体越大的软件被清理的可能性越大;
4、一些预装软件,合作厂商会限定软件大小;
5、APP经过多次版本迭代,产生不少冗余代码和无用资源,会带来更高的学习和维护成本,也更容易出错。
文章将分三大部分进行讲解
一、iOS安装包的构成
二、安装包缩减方案
三、相关工具和知识介绍
1
iOS安装包的构成
选择ipa安装包,右键打开压缩包可见,iOS安装包主要由三部分组成:二进制可执行文件、资源文件(图片和视频)和其他文件(sdb数据库、plist文件等)。
以腾讯手机管家iOS版本安装包为例:发布包解压缩后是39.6M,其中二进制可执行文件共25.54MB占比64%,资源文件8.63MB,占比22%,其他文件大小总和占比14%。
2
安装包缩减方案
从第一部分可以看出,二进制可执行文件和资源文件是缩包方案的重点,下面分别从资源文件瘦身和二进制可执行文件瘦身两个方面进行详述。
一、资源文件瘦身
去掉无用资源
① 无用图片
原理:扫描项目里面所有png和jpg图片,然后在二进制文件、plist、xib里面匹配文件名字符串,得出图片没被引用
具体方法:在项目工程中,一般使用到图片资源均会在代码中使用字符串常量来引入图片资源。那么通过otool命令逆向__TEXT__cstring段来获取二进制文件中所有的字符串常量,并检查这些字符串常量是否匹配安装包中任意图片资源名(去除文件后缀,如@3x.jpg)。之后,没有被匹配的安装包中的图片资源就标记为疑似无用图片,然后做进一步排查处理。
因为在代码中,通过一些拼接字符串引用的图片资源在此方法中会被标记为无用图片,而实际中是有被使用到,这部分需要开发排查。
② 删除/压缩无用的视频文件、字体文件等其它资源
③ 删除曝光率低功能或者变成网页形式
压缩资源
imageOptim进行无损压缩(主要是如文件的EXIF标签、颜色配置文件、作者等信息)。
imageAlpha进行有损压缩。
二、可执行文件瘦身
删除无用类
随着代码工程越来越大和研发历史越来越长,在工程中会存在一些类并没有被使用,而Objective-C的动态性,编译器会把项目里所有OC源文件编进可执行文件里,那么删除这些无用类文件必定能减少二进制文件的大小。取全部类列表classlist和在工程中被引用的类classrefs之差,就是无用类。
具体方法为:
① 通过otool命令逆向__DATA.__objc_classlist段和__DATA.__objc_classrefs段来获取当前所有oc类和被引用的oc类,其结果中包含类的段地址。
② 通过脚本解析,抽取出如上两个结果文件的差,也就是所有无用类的段地址集合
③ “otool -o 二进制文件” 命令获得所有段的详细信息
④ 将步骤2中对应的段地址作为参数,在步骤3的结果中脚本解析出具体的类名
删除无用方法
由于Objective-C的动态性,它可以通过类名和方法名获取这个类和方法进行调用,所以编译器会把项目里所有OC源文件编进可执行文件里,哪怕该方法没有被使用到,删除这些无用方法必定能减少二进制文件的大小。取全部的类方法和被引用的类方法selrefs之差,就是无用方法。实践证明,这种方法会比较多,那么可以做个类方法大小排序,去处理大于一定阀值的无用类方法。
具体方法为:
① 用脚本从linkmap文件中解析二进制文件中所有的类方法(通过解析text代码段内容)
② 通过otool命令逆向__DATA.__objc_selrefs段获得被引用的类方法
③ 如上两个结果文件相减就是没有使用到的类方法
④ 步骤3结果结合linkmap文件,可以获取每个类方法的大小,并依据方法大小对类方法进行排序
监控可执行文件大小
对linkmap文件进行分析,根据序号累加每个obj文件在每个段的占用大小,从而计算出每个obj文件在可执行文件的占用大小,进而算出每个静态库、每个功能模块代码占用大小(__DATA.__bbs是代表未初始化的静态变量,Size表示应用运行时占用的堆大小,并不占用可执行文件,所以计算obj占用大小时,要排除这个段的Size)
具体方法为:
① 分别获得版本1和版本2中各个文件的大小
② 用脚本获取两个版本中文件大小的差值
③ 对步骤2中的结果进行排序和具体分析
3
相关工具和知识介绍
一、Otool命令介绍
Otool可以提取并显示ios下目标文件的相关信息,包括头部,加载命令,各个段,共享库,动态库等等。它拥有大量的命令选项,是一个功能强大的分析工具,当然还可以做反汇编的工具使用。
使用方法:将安装包中的二进制文件作为otool的命令参数
二、LinkMap简介
LinkMap文件是Xcode产生可执行文件的同时生成的链接信息,用来描述可执行文件的构造成分,包括代码段(__TEXT)和数据段(__DATA)的分布情况。
使用方法:只要设置Project->Build Settings->Write Link Map File为YES,并设置Path to Link Map File,build完后就可以在设置的路径看到LinkMap文件了
LinkMap文件内容详解
① Object files
第一部分列举可执行文件里所有.obj文件,以及每个文件的编号
② Sections
第二部分是可执行文件的段表,描述各个段在可执行文件中的偏移位置和大小。
③ Symbols
第三部分详细描述每个obj文件在每个段的分布情况,按第二部分Sections顺序展示。
#Address Size File Name
0x100001114 0x00000708 [ 1] -[MQQXLoadingView initWithFrame:]
- Golang单例模式
- 2018,我要Axublog。
- 厚土Go学习笔记 | 16. go语言有指针 没有指针运算
- 嗤!给你来点fiyocms漏洞喷雾
- 厚土Go学习笔记 | 15. defer语句延迟函数的执行
- Nodejs学习笔记(九)--- 与Redis的交互(mranney/node_redis)入门
- Nodejs学习笔记(十)--- 与MongoDB的交互(mongodb/node-mongodb-native)、MongoDB入门
- Golang泛型编程初体验
- 工具| 手把手教你制作信息收集器之端口扫描
- 厚土Go学习笔记 | 14. switch 的条件写的有点灵活,不过风格还是go的一贯风格
- Nodejs学习笔记(十四)— Mongoose介绍和入门
- 厚土Go学习笔记 | 13. 用循环和函数 实现Sqrt(x)
- 代码审计| 这是一款适合练手的漏洞
- 工具| NSE漏洞审计和渗透脚本的demo
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 思科模拟器GNS3将路由器变成交换机的方法
- docker安装nginx并配置https
- Docker Swarm集群部署lnmp+redis
- Maven快速入门
- TomCat安装及快速部署
- SpringCloud+MyBatis分页处理(前后端分离)
- 手把手教你搭建SpringCloud项目
- SpringCloud的@Value注解及GitLab配置使用
- 使用 cdk8s 与 Argo CD 进行 GitOps 实践
- 设计模式 | 模版方法
- Python 函数3000字使用总结
- 3D摇杆控制器一种简单实现!Cocos Creator 3D!
- 数据结构 | TencentOS-tiny中队列、环形队列、优先级队列的实现及使用
- RTOS内功修炼记(六)—— 任务间通信为什么不用全局变量?
- 程序员必备基础:加签验签