本系列 Tinker 源码解析基于 Tinker v1.9.12
前面讲到了 Tinker 安装补丁的流程,现在就详细地来看下 dex 合成的代码。代码入口就在 DexDiffPatchInternal.tryRecoverDexFiles 中。
UpgradePatch
1 | //we use destPatchFile instead of patchFile, because patchFile may be deleted during the patch process |
直接调用了 DexDiffPatchInternal.tryRecoverDexFiles 方法。
tryRecoverDexFiles
1 | protected static boolean tryRecoverDexFiles(Tinker manager, ShareSecurityCheck checker, Context context, |
tryRecoverDexFiles 方法开头做了些校验,最后又到 patchDexExtractViaDexDiff 中。
patchDexExtractViaDexDiff
1 | private static boolean patchDexExtractViaDexDiff(Context context, String patchVersionDirectory, String meta, final File patchFile) { |
在 patchDexExtractViaDexDiff 中可以看到, dex 文件合成之后,会对其做 opt 优化。而合成的代码就在 extractDexDiffInternals 里面。
extractDexDiffInternals 方法有点长。按照老规矩,我们分段来看。
extractDexDiffInternals
1 | private static boolean extractDexDiffInternals(Context context, String dir, String meta, File patchFile, int type) { |
首先读取 dex_meta.txt 中的信息,用“,”分割,保存到 patchList 中。
下面贴出一份 dex_meta.txt 的示例:
1 | classes.dex,,1a6e6d6a40eff95aa33ab06e07acd413,1a6e6d6a40eff95aa33ab06e07acd413,d865f383455abd6e3f70096109543644,2999635299,712828526,jar |
dex_meta.txt 记录着
- name :补丁 dex 名字
- path :补丁 dex 路径
- destMd5InDvm :合成新 dex 在 dvm 中的 md5 值
- destMd5InArt :合成新 dex 在 art 中的 md5 值
- dexDiffMd5 :补丁包 dex 文件的 md5 值
- oldDexCrc :基准包中对应 dex 的 crc 值
- newDexCrc :合成新 dex 的 crc 值
- dexMode :dex 类型,为 jar 类型
接着往下看。
1 | File directory = new File(dir); |
然后获取基本包和补丁包的路径,为下面合成做准备。
1 | // 遍历 ShareDexDiffPatchInfo |
从这里开始,就是遍历 patchList 中的记录,进行一个个 dex 文件合成了。一开头会去校验合成的文件是否存在,存在的话就跳过,进行下一个。
1 | ZipEntry patchFileEntry = patch.getEntry(patchRealPath); |
如果 oldDexCrc 为0,就说明基准包中对应的 oldDex 文件不存在,直接按照 patch 信息重新打包 dex 即可。
1 | // 如果 dexDiffMd5 为 0, 就说明补丁包中没有这个dex,但是基准包中存在 |
上面这段代码用来处理基准包中有 oldDex ,但是补丁包中没有 dex 的情况。
如果是 dvm 环境就跳过不处理即可,如果是 art 环境就把 oldDex 复制过去。
1 | else { |
最后,就是基准包和补丁包中都存在对应 dex 的情况了。
代码一开始就是一堆的各种校验,都通过后,调用 patchDexFile 执行合成操作。合成完后再对合成的 dex 进行md5校验。
patchDexFile
1 | private static void patchDexFile( |
在 patchDexFile 中,拿到基准包 dex 文件的 InputStream 和补丁包 dex 文件的 InputStream ,然后利用 DexPatchApplier 把这两个流合成一个 dex 文件。
1 | public void executeAndSaveTo(OutputStream out) throws IOException { |
而 DexPatchApplier 里面合流操作的代码是需要根据 Tinker 的 DexDiff 算法来的。大致就是把两个 Dex 文件的每个分区做 merge 操作。
这里先留一个坑。等以后把 DexDiff 算法看明白了再补上。
另外,dodola 写了一篇 Tinker Dexdiff算法解析,有需要的同学可以看下。
那么 dex 合成的流程就到这吧。