前言
首先我们思考一个问题,为什么要解析安装包?目的是什么?什么原因促使我们做这件事?
- 减小包的体积,产品或运营同学认为,包体积越小,越能提高下载量
- 应用市场限制,如App Store、Google Play 都有相关包体积的规定,都是以更小为主
- 减少内存占用,不管是Rom还是Ram 肯定是随着应用包的体积增加而成正比增加,所以减小包体,也是在间接优化内存占用
我们找了这么几个原因,促使我们做这件事,确实在实际的工作中,特别是toC的小伙伴,更加明显,他们一直在做包体积的优化,那么要优化体积,那肯定是要知道包的构成,这样才能有合适的优化方法,那么我们再来看看包的构成。
<a name="3FVJ8"></a>
包的构成分析
我们利用Android studio的可视化工具,打开了一个普通的apk安装包,如下 <br />
- Dex、so库。且WX的SO库只保留了armeabi-v7a架构的包,已占比50%,可见很高。
- r资源文件及assets ,存放图片,音频,资源文件的位置
- resources.arsc 文件也达到了6.8MB,这是资源索引表,开发中Resources就是通过resources.arsc把Resource的ID转化成资源文件的名称,然后交由AssetManager来加载的,它是由AAPT工具在打包过程中生成。
- META-INF 签名信息
- AndroidManifest.xml 清单配置文件
整体来看,占比较高的就是 Dex、So、r、assets、resources.arsc,那么我们优化,肯定是要从这几个方面入手对吧。
<a name="AN1Z1"></a>
如何减小安装包的体积
1.资源压缩
对大图进行无损压缩,对不需要alpha通道的png图,压缩成jpg,或者使用更小的webp图片:webp介绍<br />对于assets中存储的音频文件可以选择远程依赖,第一次下载后做缓存处理
2.通过编译器缩减,混淆
利用R8 编译器,进行代码缩减、资源缩减、混淆处理等,都可以有效的减小包体积,具体介绍请看:缩减、混淆处理和优化应用<br />注意:R8编译器要求 Android Gradle 插件 3.4.0 或更高版本
3.resources.arsc文件缩减
这个文件怎么缩减呢?经过查询资料发现,该文件对于不同的语言,不同的编码格式有一定的影响,直接说结论:
- 对于纯英文来讲,建议使用utf-8格式编码
- 对于中文来讲,建议使用utf-16
具体实现操作请看: aapt 相关命令
4.so库精简
通过上面第二张图我们发现,wx的so库只保留了armeabi-v7a架构,这也是目前最流行的架构,wx这么大的用户量都敢只保留一个,你有啥不敢的。将x86、arm64-v8a果断删了吧。这是表面的优化,更深层次的就需要对so库代码精简,如:抽离独立的库,减少冗余代码。还有建议C++运行时库使用stlport_shared,同样可以减少包大小,且可以节省一点内存,这种方式请注意:应用程序需要先加载所需要的共享库,然后再加载依赖此共享库的其他原生模块
static{
System.loadLibrary("stlport_shared");
System.loadLibrary("xxxxx");
}
5.Dex文件数量优化
在我们使用multiDex后,或者说方法数达到65535之后,不得不对代码进行分包,分包会带来什么问题呢?
- method id 分配不合理导致更多的Dex量,由于method id 的大量冗余导致每个 Dex 真正可以放的 Class 变少。
- 信息存储冗余,因为每个dex中都存在调用的方法的详细信息,举个例子,如果一个class method被其他dex引用到的话就会导致 这个class不光是在自己的dex中存在方法信息,被引用到的dex中也存储了class的方法信息,这样造成冗余,冗余过多就会导致dex数量增加。
知道了问题如何解决呢?答案就是尽量让方法的引用都在同一个dex中,这样就可以减少冗余,减少dex的新增,目前最优解建议使用:Facebook 的一个开源编译工具ReDex,具体使用方法建议去看文档:https://github.com/facebook/redex,这里就不展开描述。
6.Dex压缩
此方法还是来源于Facebook的包,它真正的dex代码放到了assets目录,且通过xz 压缩算法(该算法压缩率比 Zip 高 30% 左右),并通过应用首次启动的时候解压缩,并利用多线程解压缩方式,耗时并没有那么明显。
小结
说了这么多的优化放法,如果想做到极致,肯定还有方法,但我们现在处于5G时代,大家还会对10M甚至说100M有感觉吗?这就需要于用户体验之间做一个权衡,一些极致的优化肯定是会降低用户体验的,需要按需而行吧。
Matrix App Checker
终于进入正题,我们了解了包的结构和常见的缩减方法,那么App Checker到底可以为我们提供什么样的帮助,来辅助我们进行缩减呢?随我一探究竟。
代码目录
<br />可以看到libs中引入三方jar包- apktool-lib-2.4.0.jar , 它的作用就是将apk反编译出来,产出dex、libs、manifest等文件。再往下看代码
- exception目录中 抽象了两个 TaskExecuteException、TaskInitException异常,任务执行和任务初始化异常,方便捕获。
- job目录中 抽象出 ApkJob 来管理所有的 Task 任务和 JobResult
- output 目录 主要作用就是将输出的结果 以Json或者html格式的方式写入文件中
- result 目录 对JobResult、TaskResult的抽象及相关实现
- task 目录 所有任务的实现,包括 CountClassTask 统计类数量、MethodCountTask 统计方法量、UnzipTask 解压任务负责将apk解压成相关文件。
- ApkChecker 负责创建Job,然后调用run方法。
类图
文字描述总显得有些乏力,画一下类结构图来帮助我们理解代码。