一、根源
随着业务不断迭代替换,App的大小也在快速提升 ,2019年~2022年期间一度超过了117M,期间我们也做了部分优化如图1红色部分所示 ,但在做优化的蓝莓市场外汇是不是正规的并且面临着新的增量代码 ,包体积一直连续上升 。包体积直接或间接地作用着获取转化率、安装时间、磁盘空间等关键指标 ,所以投入精力发掘更深层次的安装包体积优化是十分必要的 。根据谷歌商店的内部数据,APK体积每下降10M,平均可提升~1.5%的获取转化率 ,如图2所示 :
图1 京东金融平台2019-2022体积变动流程 (红色部分是期间做的部分优化 ,但是很快就反弹回去了)
图2 谷歌商店软件转化率提升幅度 / 10M [1]
因此2022年9月开展我们针对金融APP进行了瘦身专项整治,在不思考增量的现状,无删减业务代码的现状下实现从117M瘦身至74M,在本次安装包瘦身流程中我们遇到了不少坑,并且也积累了些经验 ,在此复制给大家 。
二、APK解读
接下来我们会简单解读下 Apk内各组成部分,以及 Apk 作为 ZIP,其标准架构是什么样的,为包瘦身的方向设定及使命拆解供给数据支撑。
2.1 APK素材解读
图3 APK 架构
•.dex APK 中可能涵盖一个或多个 .dex 资料,软件程序内的bbmarkets蓝莓 Java/ 源码最后会以字节码的方法存在于 .dex 资料中 。
•.软件在编译资源会将一些资源或者资源索引打包成.arsc 。
•res/ 源码工程中 res 目录下除了 外的TMGM外汇开户资源资料 ,这些资料路径并且会记录在 .arsc 中 。
•lib/ ,即源码工程 jni 目录下的 so 资料,二级目录为 NDK拥护的 ABI。
•/ 与 res/ 资源目录不同,/ 下的资源资料不会在 .arsc 中生成查询条目,且 / 下的资源目录可完全自定义,在程序中通过 对象来获取 。
•META-INF/该资料夹下主要涵盖 CERT.SF 和 CERT.RSA 签名资料, 以及 .MF 清单资料。
•.xml 软件清单资料 ,用于叙述软件基本数据 ,主要包括软件包名 、软件id 、软件组件、所需权限、终端兼容性等。
2.2 SDK大小解读
通过我们自研的能效提高平台[7],可以直观地看到SDK的大小,如图4所示 :
图4 SDK大小排序(涵盖平台号)
图5 SDK中涵盖的SO库列表及大小
根据SDK解读后结合业务 ,来判断哪些业务适合做插件化 ,进而直观的降低包体积 。
2.3 ZIP架构解读
可以用命令输出压缩包中每个资料的具体数据日志 ,用法 : -l --t --h test.apk >test.txt
输出的日志资料进入如图6所示,每个资料的压缩数据一行,包括资料名 、初步大小 、压缩后大小等指标:
图6 APK内资料数据大小
对以上日志数据进行逐行解析 ,根据解混淆后的资料名路径、资料类别进行归类汇总,即可得出Apk的总览数据 ,包括各类别资料的数目、总大小、单一资料大小等指标 ,并设立资料大小索引。TMGM外汇官网
三 、瘦身实践
整体实行路径如图7所示 ,主要分为 :
1.常规技术计划,通过插件(代码无侵入、自动化)在编译时期完成APP瘦身;
2.进阶技术计划 ,将部分业务线差别性的通过插件化或者SO动态获取的方法就行改造,业务改造的越多,收入越高;
3.业务优化计划 ,针对业务线的数据埋点,生成访问UV进行排名 ,将UV较低的业务线反馈架构委员会 ,评估是否可以进行下线或者通过进阶技术计划(2)进行改造,进而减小包体积。
图7 整体实行路径
3-1 常规技术计划 3-1-1 图像应对
经过上述的APP的剖析,得出占用体积第一大的还是图像 ,因此将APP所有含SDK内所有图像在编译打包流程中通过瘦身使命自动完成图像优化应对 ,整体优化计划如图8所示 :
图8 图像优化计划
1.多 DPI 优化:
为了适配各种不同分辨率或者模式的终端 ,为开发者设计了同一资源多个配置的资源路径 ,app 通过 获取图像资源时,自动根据终端配置启动适配的资源 ,但这些配置伴随着的难题就是高分辨率的终端涵盖低分辨率的无用图像或者低分辨率的终端涵盖高分辨率的无用图像。
一般现状下,针对国内软件市场,App 为了下降包大小 ,会选用市场占有率最高的一套 dpi( 精选 )兼容所有终端 。而针对海外软件市场的 APP,大多会通过 打包提交至 Play,能够享受动态分发 dpi 这一作用,不同分辨率移动设备可以获取不同 dpi 的图像资源 ,因此我们需要供给多套 dpi 来满足所有终端 。在项目中 ,我们的图像有的只有一套 dpi,有的有多套 dpi ,针对上述两种场景 ,我们分别在打包时合并资源、复制资源 ,下降了包大小。
2.转换为webp格式:
是谷歌供给的一种拥护有损压缩和无损压缩的图像资料格式 ,而且可以供给比JPEG或PNG更好的压缩 。在 4.0(API level 14)中拥护有损的WebP图像,在 4.3(API level 18)和更高平台中拥护无损和透明的WebP图像
因此:我们采用插件在编译时期仅保留针对图像通过供给的shell程序进行格式转换 ,转换成功删除旧的图像,进而达到APK瘦身的效果
3.png压缩
是一个_好用的png压缩软件一个,可以进行有损图像压缩的命令行软件,因此在1和2应对终止后,可以利用进行二次压缩,达到更优的图像瘦身。
3-1-2 R资料内联优化
DEX里是Java/ 源码编译后的字节码资料 ,对DEX的优化其实就是怎么优化字节码资料 ,DEX中涵盖大量的资源索引R资料,这里主要讲下如何通过资源ID内联后进行R资料删除,达到APK瘦身的目的 :
R资料瘦身的可行性解读
日常开发阶段,在主工程中通过R.xx.xx的方法引用资源,经过编译后R类引用对应的常量会被编译进class中 。
();
这种变动叫做内联,内联是java的一种机制(如果一个常量被标记为 final ,在java编译的流程中会将常量内联到代码中,下降一次变量的内存寻址)。非主工程中 ,R类资源ID以引用的方法编译进class中,不会产生内联。
(R..);
产生这种状况的原因是AGP打包软件引发的。具体细节,大家可以去查阅一下 在R资料上的应对流程 。结论 :R类id内联后程序可运行,但并非所有的工程都会自动产生内联状况,我们需要通过技术手段在合适的时机将R类id内联到程序中 ,内联完成后,由于不再依赖R类资料,则可以将R类资料删除,在软件正常运行的并且,达到包瘦身目的 ,如图9所示 ,在编译完成后会产生大量的R资料:
图9 项目R资料生成示意
整体计划如图10所示:
图10 R资料优化流程
注意事项 :在替换阶段一定要加入二次检查,防止替换完,运行时出现异常,如下所示:
try {
int value = .(type, name);
}catch ( e){
= " is not found(I),="++",="+owner+"."+name;
throw new ption();
try {
int[] value = .(type, name);
}catch ( e){
= " is not found(I[]),="++",="+owner+"."+name;
throw new ption();
3-1-3 进行资源混淆
1.资源启动流程解读
开发流程中我们通过aapt生成的R.java中的常量来利用资源,而在编译之后利用常量的地方都会被替换为常量的值,如下所示:
final View = .(, , false);
也就是说我们通过利用一个int数值来查找利用资源。那么是怎么通过int数值找到具体的资源呢 ?我们解压apk可以看到里面有个.arsc资料 ,这个资料也是由aapt生成,资料中保存着资源id和资源key的映射关系,就是按照这个映射关系找到资源的。
2..arsc:
图11是.arsc的里存储的映射关系 ,.arsc可以理解为一个资源映射数据库 ,根据ID映射其中具体的路径和名称。
图11 .arsc解析
通过解压APK后,将资源资料名进行短链应对比如res//hello.xml转换为r/l/a.xml后 ,然后更改.arsc对应的value值,达到整体的瘦身效果。
[5]是微信推出资源优化软件,它的基本思想类似于 中的混淆 ,可以实现以上计划 。
3-1-4 7zip压缩
7zip命令说明:
-t:指定压缩类别,拥护7z, xz, split, zip, gzip, bzip2, tar, ....
-m:指定压缩算法,默认是
具体流程如下 :
第一步 :利用7z命令将未签名包解压到指定目录:7za x ${未签名包} -o${7z解压目录}
第二步:首先通过7z命令对解压目录进行全部压缩:7za a -tzip -mx9 ${方向7z资料名} ${7z解压目录}
第三步:获取存储类别资料 ,通过 SDK中的aapt命令获取压缩方法为的资料列表 :aapt l -v ${未签名包}
第四步 :替换存储类别资料,通过7z命令将存储类别资料替换到第二步流程中生成的7zip安装包:7za a -tzip -mx0 ${方向7z资料名} ${存储类别资料目录}
3-1-5 配置CPU架构
根据不同的CPU架构 ,构建不同的类别的安装包,目前主流终端都是64位机器,因此安卓市场上主要投放的是依据arm64-v8a编译构建的安装包
ndk {
arm64-v8a
3-1-6 arsc 压缩
.arsc 的压缩体积收入很高,但对其进行压缩会作用开展速度和内存指标。原因是 :平台在启动 arsc 资料时,若 arsc 资料未压缩,可利用 mmap 进行内存映射;若 arsc 资料被压缩了 ,则需要将其解压缩后读取到RAM 缓冲区 ,会提升内存利用 ,也会拖慢开展速度 。
官方出于同样的思考,从 >=30后不能用这种方法 开展强制要求.arsc ,否则会直接安装失败,因此本文不在展开阐述。
3-1-7 国际化语言应对
京东金融App目前仅在国内市场运营 ,但是接入的大量SDK中加入了几十种语言一样,引发整个体积变大,经过评估可以通过配置 去除无用的语言资源 。
{
"zh","en"
3-1-8
:编译流程中用来检查并删除无用资源资料 ,也就是没有引用的资源
:用来开启删除无用代码,比如没有引用到的代码,所以如果需要知道资源是否被引用就要配合利用 ,只有两者都为true时才会起到真正的删除无效代码和无引用资源的目的 。
其作用是将未被引用的资源资料替换为一个体积很小的格式资料(仍存在占位体积 ,并且保留了该资源条目,所以 .arsc 体积并不会下降),可通过 res/raw/keep.xml 资料配置 和白名单。
{
{
// 不呈现Log
"", "", "false"
//混淆
true
// 移除无用的资料
true
le('-.txt'), '-rules.pro'
sign.
3-1-9 编码约束
•尽量少用枚举类别,因为枚举在编译成字节码后,会提升大量体积,如图12所示(22行代码编译后字节码是86行)
•
图12 枚举类别编译后的字节码对比
•删除不必要的LOG日志输出
3-2 进阶技术计划
SO库动态获取和插件化技术,本质上都属于动态获取的一个范畴 ,两个计划可以在业务中长期连续利用 ,在具体利用流程中如何选择 ,如图13所示:
图13 业务如何选择进阶计划
3-2-1 SO库动态启动
APP中有部分业务不适合做插件化改造 ,经过拆解发现其中的SO库占比很大 ,因此可以思考采用动态获取的方法进行改造,进而实现减小体积 。
SO库启动的两种方法
第一种方法我们直接把SO库获取并放到指定目录就可以
第二种方法是通过生态变量配置的目录中进行启动SO库,因此我们需要追加指定的目录到生态变量中 ,就可以正常启动SO库
.load("{保养路径}/.so")
.load("xxx")
1、如何配置APP中SO库的生态变量位置(借鉴):
final Field = .(, "");
final = .get();
final Field ries = .(, "ries");
List = (List) ries.get();
if ( == null) {
= new (2);
final = .();
while (.()) {
final File = .next();
if (.()) {
.();
break;
.add(0, );
final Field = .(, "");
List = (List) .get();
if ( == null) {
= new (2);
final List = new (.size() + .size() + 1);
.();
.();
final = .(, "", List.class);
final [] = ([]) .(, );
final Field ments = .(, "ments");
ments.set(, );
2 、如何删除指定SO库和整个启动流程,如图14所示:
图14 SO库删除和启动流程
3-2-2 插件化
什么是插件化 :
插件化是将一个Apk根据业务作用拆分成不同的子Apk(也就是不同的插件) ,每个子Apk可以独立编译打包 ,最后发布上线的是集成后的Apk 。在Apk利用时 ,每个插件是动态启动的 ,插件也可以进行热修复和热替换。
•宿主 :主App可以用来启动插件也成为Host
•插件 :插件App ,被宿主启动的App,可以跟普通的App一样的Apk资料
什么方法的业务适合插件化改造:
•业务相对独立,与宿主App解耦彻底
•改造成本低 ,收入相对较高
•占用体积较大
经过一些列评估,影片营业符合以上几点,改造后的效果如图15所示:
图15 影片营业厅插件化改造后效果
3-3 业务优化计划
随着业务越来越多 ,一些陈旧的业务UV越来越低 ,因此制定了一套业务下线优化流程,如图16所示:
图16 业务优化计划流程
四、管控
瘦身计划的实行很关键,后续的管控不反弹更关键,我们一边做瘦身治理,另一边探索常态化的管控机制,最后沉淀了一套管控规范和管控机制。管控的目的不是限制业务迭代或者新增代码,而是怎么做到在有限的代码中实现其作用,提高工程师日常编码中的瘦身意识 。
4.1 SDK接入规范
为防止SDK无序扩张 ,制定了SDK准入规范,在保证作用的前提下严控SDK体积大小,最大程度运维APP体积反弹 。
4.2 管控流程
图17 管控流程
根据提升素材、删除素材、增大素材、减小素材 、重复资料、代码治理等资源资料的变动现状结合治理管控规范等进行治理 ,打包构建完成会跟历史平台就行差量对比,获取变动的素材来评估是否具有优化空间,并给出优化方向 ,待优化后再次构建打包集成 。
五 、成果与后续规划 5.1 成果
通过以上措施,京东金融平台经过两个季度5个平台的迭代,从117M到现在的74M(图18) ,整体一直维持在可控的规模内 。并且在接下来的平台迭代中,我们会将APK瘦身常态化,始终维持包体积在可控的规模内 。
图18 金融APP瘦身成果
5.2 后续规划
连续技术手段优化:
业务的不断堆积迭代,总会产生一些无用的资源,所以安装包瘦身要定期清理这些无用资料和代码;
做好各个平台的监控 ,对比平台之间的差异 ,发现可以在不作用业务现状下,利用技术手段优化。
线上管控平台搭建:
前期采用线下的管控治理 ,实行起来有点耗时,后续我们会完善线上管控平台的搭建,与整个App发布构建平台进行融合 ,形成流水线的机制,做好管控。
小结:安装包瘦身的探索还有很长的路走,本文也只是列举了一些常用的瘦身计划,对于庞大的项目除了优化外,还有做好项目之间的治理,连续对APP进行体积优化,提高读者体验。
【参考资料】
[1] 包大小与安装转化率
[2]
[3] R8
[4] 与R8对比
[5]
[6] AGP
[7] :基于去中心化技术的研发 、测试阶段能效提高软件