APK 瘦身之路
先说一下我对 apk 瘦身的看法,对开发流程有哪些实际的意义:
- 减少了 apk size,这是最明显的体验。
- 减少了资源和代码文件之后,我们开发的时候,debug能更快一点(debug 一般不开启混淆,会把全部资源和代码,进行build)
- 通过分析 apk,可以评估一些规范,例如 png 文件处理规范,评价第三方库和so的价值,更重要的是我们能够更好的清楚,我们写代码的时候的一些规范,例如是否需要创建新的 class,怎么处理废弃的 class,怎么处理废弃的资源。
这里先说下 apk 瘦身的思路,首先通过apk analyse 工具(例如AS 自带的分析工具),分析出 apk 的文件占比大小,然后根据统计结果,制定瘦身方案。
apk analyse
Android Studio Apk Analyzer
这里首先使用了AS 自带的分析工具,对线上的 apk 进行分析(酷狗8.6.8,繁星3.1.5),分析结果如下:
酷狗
繁星
上面看到的只是总体情况,下面我们进入文件占比最大的几块,进行分析,这里分析的是线上包,由于开启了资源混淆,所以这里看到的文件名都是混淆之后的。
首先这几张700Kb的大图就应该处理掉~~
APk 瘦身
代码混淆和压缩资源(ProGuard 和 ShrinkResources)
这里两个做法是 android 官方提供的做法,可以达到以下效果:
- ProGuard 会检测和移除封装应用中未使用的类、字段、方法和属性,包括自带代码库中的未使用项(这使其成为以变通方式解决 64k 引用限制的有用工具)。ProGuard 还可优化字节码,移除未使用的代码指令,以及用短名称混淆其余的类、字段和方法。
- 代码压缩器移除所有未使用的代码后,资源压缩器便可确定应用仍然使用的资源。这在您添加包含资源的代码库时体现得尤为明显——您必须移除未使用的内容库代码,使内容库资源变为未引用资源,才能通过资源压缩器将它们移除。
所以通过代码混淆和资源压缩,可以在打包的时候避免引入无用的代码(类,方法,字段)和资源(不包含string,color等)。
更好的混淆方案
这里的话,采用的是 微信团队的混淆方案。AndResGuard,这里的话,我这边正在尝试,因为涉及到多渠道打包的问题,我这边利用下班时间正在做尝试,根据微信的开发者的说明,之前这个混淆方案大约帮助微信减少了1MB的大小(运来大小35MB的样子)
图片压缩
这里采用的压缩方案是 pngquant(gui)+tinypng 下面是压缩处理的结果(包含了 appt 打包前后的对比):
参数 | apk size(压缩前/压缩后) | 资源文件大小(压缩前/压缩后) | 压缩比例(资源) |
---|---|---|---|
酷狗 | 34.8MB/34.6MB | 10.3MB/10MB | / |
繁星 | 31.4MB/29.2MB | 11.6MB/9.4MB | 18.96% |
tip:
- 使用 pngquant 压缩图片,如果图片是 jpg 格式,会被自动转为 png 格式。
- 考虑过用其它压缩工具,体验的感觉是千篇一律,而 pngquan 支持最全面。
最终的一些结果:
- 需要商议指定图片文件的准入规范,因为在压缩过程发现,繁星项目有三张接近800kb的jpg文件,这些肯定是需要压缩的。
和设计部门确认图片规范,例如需要删除 ps 处理之后的图片元数据信息,需要指定位深度,背景图的处理
图片适配的问题
drawable 文件夹的适配问题
我们都知道,android 系统会根据屏幕密度对我们的 drawable 图片进行适配,例如
这里为什么要考虑这个问题,因为你在 src 或者 background 里面使用一个图片,这里是不会自动进行 resize的,只会根据scaletype 和View大小(宽和高)对你的图片大小(分辨率)进行适配。
下面是做的一个实验,放置同一张分辨率和大小在不同的 drawable (hdpi,xhdpi,xxhdpi,xxxhdpi,nodpi)文件夹下,然后将五张图片设置为 相同大小ImageView 的src 属性,最后计算 ImageView BitmapDrawable 的大小,关键代码和drawable 文件夹情况如下:
BitmapDrawable b1 = (BitmapDrawable) imgv1.getDrawable();
tv1.setText("bitmap 大小:"+b1.getBitmap().getRowBytes());
然后得出的结果如下:
注意这里适配drawable 是根据屏幕密度,而不是屏幕分辨率,例如 nexus 5X 的屏幕密度是 420 dpi,会被当作是
这里发现的一个小问题是,在不同 SourceSet 下放置了同名的文件,是被当作同一文件的(资源id是一样的),但是ide 不会提示同名冲突的问题。
所以这里针对图片资源给出以下策略
1. 尽可能的压缩图片,制定图片资源入代码库的规范(我所在的团队使用tinypng 的 as 插件进行压缩,或者把压缩工作交付给设计师)
2. 尽量少用图片做背景,即便要用也可以考虑局部用图片
3. 尽量保持适配最常见的分辨率
Dex 方法数详解
由于单个 Dex 限制了代码引用的代用总数不能高于 65536,随着业务膨胀,方法数自然会爆炸,所以需要拆分多个 dex。
根本原因
dex 方法数目受限的根本原因在于 Dex 的文件格式,由于字节码在调用方法时,必须显示寻址方法在 dex 存储的索引,即meth@BBBB[2]。BBBB 的含义是每个四位,四个 B 就是十六位,所以最多支持 2^16 个方法。
那么这些的方法都包含了那些方法?
- 自己编写的 java 文件中的代码
- Android 框架方法
- 引用的第三方库的方法
ART 虚拟机
在 5.0 以后(API 21) 以后,android 系统会在 apk 安装时候,扫描 dex 文件,将多个 dex 文件预编译成一个 .oat 文件,方便 android 系统使用。但是在 5.0 以前的系统中,为了支持多个 dex,编译器做了很多决策去决定哪些 class 文件是包含在主 dex 中。所以这种情况下,build 的速度会比单个 dex 要慢上很多,开发模式下,你可以通过配置多个 product flavor 避免 multi dex,来提高开发速度。
如何避免
- 减少依赖文件
- 通过 proguard 移除无用的代码
总的来说,dex 爆炸不会对启动速度有太大的负担,主要是从 apk 大小考虑的话,可以避免无谓增加方法数的做法。
参考文档: