MultiDex对于很多开发者来说应该并不陌生,大一点的项目应该都已经遇到了INSTALL_FAILED_DEXOPT 的问题,因为当 Android 系统安装一个应用的时候,有一步是对 Dex 进行优化,这个过程有一个专门的工具来处理,叫 DexOpt。DexOpt 是在第一次加载 Dex 文件的时候执行的。这个过程会生成一个 ODEX 文件,即 Optimised Dex。执行 ODEX 的效率会比直接执行 Dex 文件的效率要高很多。
导致这个错误的原因有两个
单个 dex 文件方法总数 65K 的限制。
DexOpt 会把每一个类的方法 id 检索起来,存在一个链表结构里面,但是这个链表的长度是用一个 short 类型来保存的,导致了方法 id 的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。
DexOpt 的 LinearAlloc 限制。issue:22586 和 issue:78035
Dexopt 使用 LinearAlloc 来存储应用的方法信息。Dalvik LinearAlloc 是一个固定大小的缓冲区。在Android 版本的历史上,LinearAlloc 分别经历了4M/5M/8M/16M限制。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB 或16MB。当方法数量过多导致超出缓冲区大小时,也会造成dexopt崩溃
对此Android官方给出的解决方案就是采用MultiDex方案,关于MultiDex的使用方法,这里就不说了,本文主要从源码的角度来分析MultiDex的的整个工作流程
首先我们从入口方法MultiDex.install(this)
开始入手
|
|
我们进入这个方法
|
|
这里需要说明一下,在5.0以下的版本采用的是Dalvik,而5.0以上的运行时是ART。
具体代码的分析已经在上面代码的注释里给出了,从这里我们也可以看出,整个MultiDex.install(Context)
的过程中,关键的步骤就是MultiDexExtractor#load
方法和MultiDex#installSecondaryDexes
方法。
先看MultiDexExtractor#load
|
|
再看MultiDex#installSecondaryDexes
|
|
因为在不同的SDK版本上,ClassLoader加载dex文件的方式有所不同,所以这里做了V4/V14/V19的兼容。
这里主要看下 V14
|
|
通过调用DexPathList#makeDexElements方法,可以加载我们上面解压得到的dex文件,我们看下V14的DexPathList
源码(只看核心代码)
|
|
我们为什么要反射pathList
字段呢?ClassLoader 在findClass()
时会在pathList
中寻找,因此通过反射手动添加其他Dex文件中的class到pathList
字段中,就可以实现类的动态加载,这也是MultiDex方案的基本原理。
至此,MultiDex的源码分析到这里就结束了,其实有很多概念还没有讲清楚,只是讲了个大致思路,后面有时间再来补充吧