前言
MultiDex是为了解决65536的问题,虽然说现在我们使用起来很方便了,作为一个有追求的程序员,使用并不是我们的目标,我们的目标是学习其实现原理。
MultiDex#install
|
|
这个方法的所有代码都在上面,代码不错,逻辑如下:
- 如果当前虚拟机已经支持MultiDex了,直接退出
- 首先获取apk文件的路径,并加到installedApk中,防止重复install
- 获取classloader,这里的这个classloader是BaseDexClassLoader,要记住,我们后面会用到。
- 清除secondary-dexes文件夹中的dex文件
- 通过MultiDexExtractor去提取dex文件,注意这时的第四个参数是false,
- 校验有效性
- 合法installSecondaryDexes,安装
- 不合法,再次提取,不过这次提取就是从apk文件中提取了,进行安装。这一步是一点补救措施。
接下来便对其中一些关键步骤进行分析。
MultiDexExtractor.load
|
|
其中dexFile 路径为code_cache/secondary-dexes
在这一步是对dex文件进行提取。其中的逻辑如下,
- 获取apk文件的crc校验码
- 如果不是强行reload(强行reload是指直接从apk文件提取),并且apk文件没有进行修改。
- loadExistingExtractions 提取缓存
- 如果失败,则从apk文件中提取
- 当设置了forceReload时,直接从apk文件中提取
获取apk文件crc校验码
|
|
在这个方法中,通过ZipUtil去获取crc校验码。我们看下他的实现,
|
|
通过findCentralDirectory去返回一个CentralDirectory对象,这存储了内容的偏移量和size两个字段。关于如何计算,详细看代码和zip文件格式。ZIP文件格式
CRC计算就略过了。
isModified
|
|
这里通过比较文件更改时间以及 crc校验码来判断apk文件是够进行过修改。如升级。
loadExistingExtractions
直接加载缓存好的dex文件。
|
|
上面的代码,直接查找code_cache/secondary-dexes 目录下的dex文件。
performExtractions
这个方法是干嘛的呢?当我们load缓存出异常的时候,就会用apk文件中做提取,我们看下逻辑。
|
|
代码也比较简单,提取文件嘛,相信大家都会的,这里就不啰嗦了,哈哈。
putStoredApkInfo
从apk文件提取完之后呢,就会将apk最后一次修改时间,crc校验码,dex文件数目等信息,存放到SharedPreferences中,以便以后调用。
MultiDex#installSecondaryDexes
|
|
安装这里呢,我想大家都比较熟悉了,看了太多这样的代码。一V19为例。
在看代码之前,我们需要了解一点小知识。在BaseDexClassLoader中,有个DexPathList类型的字段,而DexPathList中,有个Element[],dexElements,这个就是我们的dex文件对应的,当findclass的时候,会依次去里面每个元素进行查找。多的不说了,具体去了解下类加载。
|
|
- 首先通过classloader获取到pathList,再通过DexPathList的makeDexElements方法构造一个Element[]数组,注意,不同版本的方法名和字段名可能不一样,这里以5.0位例的。
- 通过expandFieldArray方法,去扩展原先的Element数组,将除了第一个dex文件外的其他dex文件添加到里面。
- 略过。。。
大体的流程就是这样的。
总结
看了简单的源码之后,还有几个问题需要我们思考:
- clearOldDexDir 目的是什么?节省空间么?
- 其中,做了哪些异常处理,比如异常重试,这个有必要学习下,是一种保护措施
- 都说在首次加载会出现耗时的情况,没看到相关的Dex优化的代码啊。
- odex和dex,odex是优化过后的dex,其后缀也可以是dex,code_cache/secondary-dexes中,就是优化过后的dex存放路径。
关于第三点,是在DexFile的构造函数中,会调用native的方法,做一些优化,比如opt,或者是oat
如果错误或者理解片面的地方,请各位同学赐教。