问题说明
在Android 升级到 5.0 (Lollipop/L)之后,虚拟机实例换成了ART,这加快了应用运行时的速度,但是在系统升级中却引入了一些不便之处:
- 在生成升级包时,因为ART采用了预编译优化功能,会把 APK 及JAR等通过dex2ota预编译成odex文件,这样极大的增加了升级包的大小,动辄上G的大小不方便用户的下载和网络的传播;
- 如果不进行预编译优化,则这部分操作会转移到刷机完成后第一次开机时间,十几分钟甚至更多的时间,让用户不确认是否升级出问题;
预编译配置-兼顾大小和时间
上述问题,Android系统设计者,早已考虑周全,尤其是针对存储空间有限的设备,提供了丰富的编译配置选项,方便开发者根据自己实际情况进行针对性的配置修改;
下面就针对这些配置选项进行说明和标记,方便后续开发人员参考;
打开odex编译优化
首先,可以完全关闭预编译优化功能,跟dalvik虚拟机时代的升级包相同:
当然,这样的结果就是升级包小了,第一次开机时间极其慢;
预编译的包不进行优化
预编译的包是指那些在模块编译文件中指定为:include $(BUILD_PREBUILT) 的APK和JAR包等,这在升级包中占了一大部门;
DONT_DEXPREOPT_PREBUILTS 变量就可以配置这部门代码是否进行预编译优化;
只进行BOOT.img 优化
Android启动过程中,boot.img包含了大部分底层系统的启动和初始化,主要包含在boot.art文件中;
使用 WITH_DEXPREOPT_BOOT_IMG_ONLY 编译变量,可以控制编译系统只进行boot.img的优化;
使能该变量后,将会大量节省system分区的大小,但同时意味着所有的app都需要在第一次重启的时候进行odex优化;
所以最好是通过 DONT_DEXPREOPT_PREBUILTS 进行更精确的控制;
LOCAL_DEX_PREOPT 编译变量使用
每个app应用,可以通过该编译变量进行控制是否进行odex优化;
在 app’s Android.mk,进行变量赋值 LOCAL_DEX_PREOPT := false
PRODUCT_DEXPREOPT 编译变量使用
在 post-L 发行版本之后,添加了这个系列的编译变量,进行更深入控制预编译的优化;
- PRODUCT_DEX_PREOPT_BOOT_FLAGS 传递参数给 dex2oat 命令,控制boot.img的编译
- PRODUCT_DEX_PREOPT_DEFAULT_FLAGS 传递默认参数给 dex2oat 命令,控制除了 boot.img的编译,即jar包和apk文件1$(call add-product-dex-preopt-module-config,services,--compiler-filter=space)
或者 直接关闭模块的预编译优化
Android 7.0 之后的优化
7.0后,系统可以分为A/B两个分区,OTA升级以及APK的优化不影响用户前台的正常使用,这样既减小了升级包的大小,又提升了第一次开机速度;
预加载类文件
预加载类会由zygote统一初始化,这样使后续使用到这些类的app开启速度加快;但是预加载类的使用在实际项目中要非常注意,需要仔细评估后确认权衡值,太多的预加载类会导致一些不常用的类消耗宝贵的内存资源,相反则会导致常用的类在不同应用中独立加载,各自有一个备份,既减慢应用开启速度,也是浪费内存;
预加载类的定义列表,默认放置在 frameworks/base/preloaded-classes ,也可以在产品的devic.mk中添加自行定义的预加载列表:
编译类列表
结合 PRODUCT_DEX_PREOPT_BOOT_FLAGS 和 compiled-classes 可以控制编译系统是否在预编译优化中编译这些类;
不过上述列表只能定义启动类的一个子集,适合用于那些存储空间非常有限,不能全局优化编译整个boot.img 分区的设备,所以对我们来说并不适合;