Apk在安装(installer)时,就会进行验证和优化,目的是为了校验代码合法性及优化代码执行速度,参见Dalvik Optimization and Verification Withdexopt。
验证和优化后,会产生ODEX文件,运行Apk的时候,直接加载ODEX,避免重复验证和优化,加快了Apk的响应时间。
先来看一张ODEX文件的结构图:
ODEX文件的结构图下面将围绕这张图,结合dexopt的代码,说明ODEX的生成过程。
一. 首先将一个空的DexOptHeader写入ODEX文件
dexOptCreateEmptyHeader(cacheFd)
二. 从Apk中提取classes.dex,追加到ODEX文件
dexZipExtractEntryToFile(&zippy, zipEntry, cacheFd)
此时文件结构是这样的:
三. 修改Dex内容
rewriteDex(((u1*) mapAddr) + dexOffset, dexLength, doVerify, doOpt, &pClassLookup, NULL)
这一步是验证和优化的核心,具体见:
verifyAndOptimizeClasses(pDvmDex->pDexFile, doVerify, doOpt)
...
verifyAndOptimizeClass(pDexFile, clazz, pClassDef, doVerify, doOpt)
...
dvmVerifyClass(clazz)
...
dvmOptimizeClass(clazz, false)
其中dvmVerifyClass和dvmOptimizeClass都是针对类中的方法,具体做了哪些事情参考源码。
四. 因为3修改了Dex内容,所以要重新计算Dex的checksum
updateChecksum(dexAddr, dexLength, pHeader)
五. 往ODEX文件后面追加Dependenices内容
writeDependencies(fd, modWhen, crc)
所谓的Dependenicies,是指Dex文件之间的依赖,比如App会依赖于framework的Dex。当framework的Dex发生变化时,App的ODEX文件将失效,需要重新生成。
writeDependencies中将bootclasspath下的dex文件都加入到依赖中。
此时文件结构是这样的:
六. 将优化的其他内容追加到ODEX文件
writeOptData(fd, pClassLookup, pRegMapBuilder)
其中按照不同类型的内容,分为三个chunk写入:
writeChunk(fd, (u4) kDexChunkClassLookup, pClassLookup, pClassLookup->size)
writeChunk(fd, (u4) kDexChunkRegisterMaps, pRegMapBuilder->data, pRegMapBuilder->size)
writeChunk(fd, (u4) kDexChunkEnd, NULL, 0)
此时的文件结构是这样的:
其中:
dexCreateClassLookup(pDvmDex->pDexFile)
此处根据Dex生成了pClassLookup,pClassLookup你可以简单理解为一张hash表,里面保存了classDescriptor到classDef的映射。
dvmGenerateRegisterMaps(pDvmDex)
此处根据DvmDex生成了RegisterMaps,RegisterMaps的作用是为了标记方法中寄存器引用的对象,在快速GC时,不会释放这些对象。具体参考Dalvik虚拟机中RegisterMap结构解析
七. 根据所有的内容,改写第一步中DexOptHeader的相关字段值。
至此,ODEX文件完整生成。
参考资料:
网友评论