Matrix-ApkChecker的实际应用
作者|张志阳
前 言
你是否关注过安装一个新App的时候需要多少存储空间?
你是否关注过下载一个新App的时候需要消耗多少流量?
...
每当我想下载一个新App,在应用商店点击下载却看到“空间不足”的提醒时,我的内心是崩溃的。
每当我想下载一个新App,在地铁上看着自己的流量套餐剩余所剩无几时,我的内心是崩溃的。
...
App安装包大小在日常使用中对用户的影响,直观表现在以上的两个方面(来自小编的亲身经历)。其实也会影响 App的加载速度、内存占用以及电量消耗。
伴随着App的版本持续迭代,代码量、资源文件会逐渐增多,编译后的安装包文件的大小也在逐渐增大。为了保证用户的使用体验,避免因为安装包过大,导致新用户不能/不想下载、使用时出现异常问题,经常进行安装包体积缩减是十分有必要的。
那么我们应该怎样进行包体优化呢?
在初期,我们优先想到的就是分析安装包内的文件资源,查找到占用空间较大的文件 和 名称不同但内容相同的重复文件,尽可能的压缩 & 删除,缩减包体。我们实现了整个流程,并且应用了很久,直到Matrix-ApkChecker 的出现,让我们在缩减Android 安装包体积的时候有了更多选项。
简 介
Matrix 是微信终端自研和正在使用的一套 APM(应用性能管理)系统。
ApkChecker 作为 Matrix 系统的一部分,是针对 Android 安装包的分析检测工具,根据设定好的规则检测 Apk 是否存在特定的问题,并输出较为详细的检测结果报告,用于分析排查问题以及版本追踪。
ApkChecker 的方便之处在于它虽然是Matrix 系统的一部分,但是它以一个单独的jar包形式提供,可以直接通过命令行执行, 也就意味着可以在不使用Matrix 系统情况下,单独使用ApkChecker工具,可以集成到我们自己的测试系统中,你品,你细品~
功 能
ApkChecker 一共有14个Task,包括:5项安装包基础信息的扫描 以及 9项针对特定问题的扫描。
5项安装包基础信息的扫描:
1、UnzipTask: 解压Apk、反混淆类名(通过mapping.txt), 反混淆资源(读取resMapping.txt)、对所有文件根据文件后缀进行分类 & 占用空间统计计算(TaskTypeID:1):
可以获取单文件占用空间、单文件类型总占用空间 以及 所有文件总占用空间
2、ManifestAnalyzeTask: 获取 Manifest文件信息 (TaskTypeID: 2)
利用AXmlResourceParser 解析AndroidManifest.xml文件,读到Apk 的全局信息
3、ResProguardCheckTask:判断包是否已混淆 (TaskTypeID:5)
4、CountClassTask:对Dex文件中方法按照类名/包名进行分组,并进行统计计数 (TaskTypeID:4)
利用google开源的com.android.dexdeps类库读取dex文件,统计方法数。可以获取每组 dex 中 方法数 及 总量
5、CountRTask: R文件中field数统计 (TaskTypeID:9)
通用使用com.android.dexdeps类库读取dex文件,找出R类以及field数目。可以获取每个R文件中的field数及总量
9项针对特定问题的扫描:
1、ShowFilesSizeTask: 按文件大小排序列出Apk 中包含的文件 (TaskTypeID:3)
可以设置文件大小最小阀值、升降序规则、以及指定固定的文件后缀
2、FindNonAlphaPngTask: 搜索不含alpha通道的png 文件 (TaskTypeID:6)
通过java.awt,BufferedImage类读取PNG文件判断是否有alpha通道,对于不含alpha 通道的png文件,可以通过更改格式(jpg、webp ...)来减少文件的大小
3、MultiLibCheckTask: 检查是否包含多个ABI版本的动态库 (TaskTypeID:7)
so 文件的大小会在Apk文件大小中占较大的比例,每个ABI支持一个或多个CPU指令集,可以根据实际需要,考虑减少ABI版本的动态库数量
4、UncompressedFileTask: 搜索未经压缩的文件类型 (TaskTypeID:8)
即搜索结果中的文件类型的所有文件都未进行压缩,可以考虑是否需要压缩
5、DuplicateFileTask: 搜索冗余的文件,按照文件大小降序排列 (TaskTypeID:10)
判断重复文件的方式是根据md5,可以在结果中获取每组重复文件中单文件的大小、文件列表、md5值,我们可以利用结果数据计算出可以通过删除重复文件而减小的空间大小
6、MultiSTLCheckTask: 检查是否有多个动态库静态链接了STL (TaskTypeID:11)
STL,即标准模版库,其中包含了很多常用的基本数据结构和基本算法,如果动态库静态链接了STL,编译后的so文件的大小会比较大,当有多个动态库都静态链接了STL时,整体占用空间可能会比动态链接STL占用空间更大,应该采用动态链接的方式而非多个动态库静态链接。通过nm工具来读取so的符号表,如果出现std::即表示so静态链接了STL
7、UnusedResourcesTask: 搜索Apk中包含的未使用资源 (TaskTypeID:12)
未使用的资源应该删除
8、UnusedAssetsTask: 搜索Apk中包含的未使用assets文件 (TaskTypeId:13)
未使用的文件应该删除
9、UnStrippedSoCheckTask: 搜索apk中未经裁剪的动态库文件 (TaskTypeId:14)
so 文件的大小会在Apk文件大小中占较大的比例,经过裁剪后,文件大小会减小很多。 通过nm工具读取动态库文件的符号表,如果结果中包含no symbols 关键字则表示该动态库已裁剪
使 用 方 法
命令行执行:
Java -jar ApkChecker.jar 查看ApkChecker 的使用说明,结果如下:
我们发现ApkChecker的命令行参数比较多,主要包括global参数和options参数两类
global参数:
option参数:
配置文件执行:
ApkChecker支持这么多参数,如果每次执行的时候,都要手动输入 或者 替换/修改一些配置信息,会比较麻烦。还好,ApkChecker 还支持指定配置文件执行,将具体的参数配置以json的格式保存在配置文件中,在命令行中使用
java -jar ApkChecker.jar --config config.json
配置文件内容参考:
其中重点说明几处断点位置的内容
当准备好以上的所需文件信息及掌握了所有配置项后,就可以使用ApkChecker进行Apk 文件的扫描了,结果文件会输出到配置的路径下,打开后进行结果分析就可以了。
在 转 转 的 实 际 应 用
1、执行方案
利用Jenkins 进行持续集成,与打包流程串联,当有新包生成并满足执行条件时,执行
JenkinsJob:
独立Job 维护,不影响打包Job 单次任务时长 及 结果判定
自定义参数,用于接收必要的执行数据,如 apk、mapping.txt、resMapping.txt、R.txt 的绝对路径 以及用于判断是否已经测试过的打包信息(Tag)
打包Job构建后操作增加shell脚本流程,将打包成功后生成的相关文件的绝对路径信息输出到指定文件中
打包Job 构建后操作增加 Trigger parameterized build on other projects ,当打包Job成功后使用指定文件中记录的参数触发ApkChecker Job
ApkChecker Job 接收参数后进行条件判断,满足指定条件则执行ApkChecker
执行条件:
每个Tag只测试一次,如果参数中的 Tag 已经测试过,则不再进行测试
执行流程:
将整个执行流程脚本化,增加测试结果文件解析、上报结果以及发送测试报告(不知道你是否注意到每一项Task后的TaskTypeID,如果你也想要解析测试结果,肯定会用到它)。
测试报告
报告邮件:
模版参考了ApkChecker的html结果文件,额外增加了结果数据的简单统计汇总 以及 测试平台上ApkChecker结果页的跳转链接
测试平台ApkChecker Tag 测试结果页:
除了邮件报告中完整的数据展示外,可以更直观的看到一些统计数据。并且增加了白名单操作,可以将一些可以忽略/筛选掉的数据,添加到白名单,在之后的测试报告中就不再显示&统计。
2、历史数据的充分利用
我们将所有的历史数据都进行了存储,希望不断的挖掘历史数据的价值,提升效率。先列举两项我们现在已经完成的功能
历史结果数据变化趋势图
可以通过选择版本区间 或者 日期时间段,查询阶段的历史结果,根据每次结果对应的Tag提交时间进行排序,展示各项结果数据的变化趋势,默认展示当前App项目一周内的数据趋势。
这个功能可以帮助我们直观的观察/监控各项数据的变化,更及时的发现数据大浮动波动,向RD询问波动原因,避免出现异常问题。
也可以随时查看某个历史阶段数据,快速的找到数据波动的节点信息,辅助追查具体原因,回顾历史版本变化。
Tag 结果数据对比
测试报告包括历史未处理问题以及新产生的问题,它们混在一起,会影响判断哪些问题需要优先解决、哪些问题可以遗留,很容易导致RD同学并不知道需要优化哪些而不主动优化,QA同学也很难判断哪些是新增、哪些是遗留,无法推动RD同学优化。
当RD同学进行过包大小优化后,虽然通过历史趋势图看到了Apk文件整体大小的变化,但是具体删除、修改了哪些内容,QA同学并不知道。通过人工对比优化前后两份测试报告的结果,很麻烦,效率很低。
为了解决上述问题,我们增加了Tag结果数据对比功能,自主选择两个对比的Tag,系统会自动将两份测试结果进行比较,展示详细的增删改详细数据信息。这样我们就可以清楚的知道RD同学都做了什么,如果有新增问题,就可以及时沟通解决
偷偷告诉你,Tag对比数据还可以这样用:
如果你的平台系统可以知道Tag的前后关系,那么当新Tag 测试完后,就可以与同分支线上的前一个Tag结果数据进行对比,将对比结果通过对比报告邮件或者办公IM消息立刻发给两个Tag diff文件的所有提交人(当然如果可以精准到问题内容的直接提交人就更好了,嘿嘿~自己的问题,当然要自己解决了,甭想赖掉哦)。当然,RD同学辛辛苦苦的优化了一些问题,如果有删除、修改 导致Apk包体缩小,也是要鼓励一下人家的。
如果你的平台系统可以知道前一版本发版时使用的Tag, 那么在新版本开发阶段,每天早上定时使用前一天的最后一次提测Tag 结果与前一版本的发版Tag结果进行对比,将对比结果通过对比报告邮件或者办公IM消息发给所有参与新版本的RD & QA,进行一次每日数据变化的汇总报告,可以提醒大家关注结果数据变化,也可以养成关注Apk包大小数据的习惯。排期太紧、任务太多、不能每个Tag都立刻解决新问题,那我们根据每日变化来决定是否有必要解决这些新增问题吧。
最 后
ApkChecker 确实比较方便、好用,安利给大家。
利用它可以建设的方案/流程,肯定远远不只上述的这些,我们也在持续探索,和RD同学们一起磨合出适合自己团队的Apk 包文件优化方案。近期我们也有了新的执行方案设计,在接下来的时间里会快速的改造上线。
如果你有对ApkChecker 或包文件大小优化有更深入的理解或者建议,欢迎在文章下方留言、评论;如果你喜欢我们的文章,请关注转转QA公众号,一键三连呦~
往期精彩回顾