Tinker补丁构建走读
Tinker简单问答
为什么要替换 Application
看上一篇和 Instant Run 的对比,可能考虑不全
为什么还要在 dexElements 前面插入而不是直接替换?
因为 Tinker 是全量合成 dex ,比如在补丁前dex顺序是这样的:oldDex1 -> oldDex2 -> oldDex3..,那么假如修改了dex1中的文件,那么补丁顺序是这样的newDex1 -> oldDex1 -> oldDex2… 那为什么不直接使用newDex1去替换调oldDex呢?我觉得:
- 运行期去替换调正在使用的dex是有风险的(也是我瞎猜)
- 考虑到版本回退和以后的增量升级,在前面插入确实比替换更加方便安全
Tinker补丁构建走读
Tinker的补丁加载网上资料很多了,读起来也没太大难度,这里就不多说了。关于补丁构建的整个过程倒是不多,这里简单走读一下。
TinkerPatchSchemaTask
关于代码的变动 dex的patch 资源的patch
这个 Task 主要是用于 oldApk和 newApk 的差分,生成patch,包括dex,res,so的差分,主要起作用的类如下:
|
|
这个 Task 也是最关键的,可以从 ApkDecoder
这个类开始,首先会先把两个apk解压到build/outputs/tinkerPatch/{variant}/apkName
目录下,在打patch 过程中tinkerPatch
目录下会生成很多过程文件,类似最终的patch apk,用于查看资源合成结果的 resources_out.zip
,还有一下log.txt。关键的patch从
|
|
这里开始,遍历newApk解压后的目录,根据Pattern
去匹配使用上面四个中哪个Decoder
去处理这两个新旧文件。
- dex查分
从UniqueDexDiffDecoder dexPatchDecoder;
处理dex开始,这里假如我们oldApk只有一个classes.dex,来到DexDiffDecoder.patch
,
|
|
这里去检查一下第一个dex中的一些限制,例如tinker的一些loader类一定要在第一个dex中等限制。
如果新增了一个dex
然后正常的修改了dex
|
|
把对应的dex保存在
然后走到dexPatchDecoder.onAllPatchesEnd();//开始生成 保存patch文件
|
|
然后来到这个
|
|
这里就是具体的dexDiff算法了,我也看不懂,就当个黑盒,反正会生产处一个查分dex保存,然后后面还会在合成一个全量dex用来查看,保存一下log日志之类的,大致的dex查分就完成了。
- res 查分
回到遍历newApk目录的地方,还是根据Pattern
去匹配资源文件,在这个方法中进行查分
|
|
还是使用了BSD进行差分,输出log,修改的文件复制到tinker_result的目录下。然后到onAllPatchesEnd
,和dex一样,还是会生成全量的resources_out.zip
去查看合成结果,还有log输出。
- so查分
这个也是使用了BSD查分,没太多好说的。
TinkerManifestTask
自动添加/修改tinker_id,运行时检查补丁版本
TinkerResourceIdTask
R.txt 的保存 ids.xml public.xml的处理,用于处理资源改变时ID变动问题,将基础包的R.txt处理成ids.xml,public.xml,保存到intermediates/res/merged/{variant}/values/
下,用于打包时候的资源ID分配。保证资源ID的不变动。
TinkerProguardConfigTask
混淆的处理,主要的作用是将tinker中默认的混淆信息和基准包的mapping信息加入混淆列表。
TinkerMultidexConfigTask
主要将dex.loader中配置的class也keep进main dex.