6.2.2 MultiDex 痛点剖析
在 Android 早期开发中使用过 MultiDex 的读者,相信会有这样一个体会,那就是这个东西不好用,会出现首次启动 ANR、闪退(报 ClassDefNoFound 的错误)等状况,而且 App 启动速度变慢,如果有统计秒开率,那么引入 MultiDex 后,秒开率会直线下降。
为什么会出现这种情况呢?下面首先从 MultiDex 做的事情进行分析,图 6-11 是启动MultiDex 的简易流程。
接下来,我们看看几个影响 MultiDex 启动的关键事件。
1. CRC 校验
通过对 dex 文件的 zip 包做 CRC 校验和文件修改日期校验,可以判断 dex 文件是否被修改或污染。 CRC 校验属于安全性和可靠性校验,耗时较长。
2.压缩 dex.zip
该过程是对提取出的 dex 文件进行单独压缩,并保存修改时间、 CRC 码,方便后续对CRC 过程进行校验,耗时较长。
3.加载 dex 文件
对 classes2.dex、……、 classesN.dex 做 dex 文件加载。 dex 文件加载过程中需要执行 DexOpt 过程,耗时较长。
根据上述分析,使用 Google MultiDex 有 3 个耗时较长的操作,分别是 CRC 校验、压缩dex 文件和加载 dex 文件。而且这 3 个操作都必须在 UI 线程中进行。因此可能出现启动速度慢,甚至启动 ANR 的情况。
以上是使用 MultiDex 导致加载慢的问题。那闪退是怎么回事呢?这事还得从分包说起。MultiDex 分包过程会优先把 Activity、 Application、 Service 等 Android 组件及其所依赖的 class文件放到主 dex 文件中,然后把部分非 Android 组件 class 文件放到其他 dex 文件中。也就是说,放到主 dex 文件中的只有启动类及其所引用到的类。
理论上, MultiDex 在 attachBaseContext 中做合成,并不会出现闪退现象,即 noClassDefFoundError 问题。但事实上,当你的 App 发布之后,会发现很多这种错误被收集上来。经过分析发现,有时为了提高运行效率,在实例化某个 class 文件的时候,会相应地对该 class 文件所引用的类做初始化。而如果这个被初始化的类位于第二个 dex 文件中,而这个 dex 文件此时还没有被加载,那么就会引发上述错误。
经过上述分析可以知道, Google 的解决方案因为启动速度慢,而且一不小心还可能出现崩溃的情况,所以不太受人待见。
6.2.3 MultiDex 方案回归
既然前面分析了 MultiDex 的很多缺点,那么为什么还会有 MultiDex 方案的回归呢?其实,原因很简单,任何技术都有它的时效性,任何技术的提出都有它的历史背景,任何技术都在往简单、好用的方面进化着。
这一切都要从 Android 5.0 ART 虚拟机替代 Dalvik 虚拟机说起。重点来了, Dalvik 使用JIT( Just In Time)编译,而 ART 使用 AOT( Ahead