2019-12-05: 说一下冷启动类加载的原理?为什么Tinker热修复需要重启应用生效?
冷启动类加载的原理
冷启动重启生效,现在一般有两种实现方式
-
一种是类似于QQ空间的插桩,单独放一个帮助类在独立的
dex中让其他类调用。阻止类被打上CLASS_ISPREVERIFIED 标志从而规避问题的出现.最后加载补丁dex得到dexFile对象作为参数构建一个Elements数组前面,这是QQ空间的做法 -
Tinker提供dex差异包,整体替换dex方案,差量方式给出patch.dex,然后将patch.dex与应用的classs.dex合并成一个完整的dex,完整dex加载得到的dexFile对象作为参数构建Element对象然后整体替换掉原来旧的dex-Element数组,这样有个毛病就算是dex合并内存消耗在vm heap上,容易OOM,最后导致dex合并失败。
dex merge 操作是在 Java 层面进行的,所有对象分配都是在 Java heap 上完成,如果进程申请超过 vm heap规定大小,那么进程就会发生OOM,系统 memory killer 可能会杀死该进程,导致dex合成失败。另外一方面,我们知道 JNI 层面 C++ new/malloc 申请的内存,分配在native heap 的增长并不受 vm heap 大小限制,只受限于RAM,如果RAM不足,导致进程会被杀死导致闪退。所以如果只是从 dex merge 方面考虑,理论上是可以的,但是实现起来非常复杂。
为什么Tinker热修复需要重启应用生效?
-
dexopt/dexoat操作是非常耗时的, 在Art虚拟机影响非常大,因为loadDex是补丁dex和APK中原dex合并成的一个完整的补丁压缩包,所以dexoat操作非常耗时.如果优化后odex文件没有生成,或者生成不完整,那么loadDex边不能在应用启动的时候进行,因为会阻塞loadDex进程,一般是主线程,所谓为了解决这个问题,我们把loadDex当做一个事务来处理,如果中途打断,那么删除odex文件,loadDex完之后,反射注入/替换dexElements数组,实现打包。如果不存在odex文件,那么重启另外一个子线程loadDex,重启之后再生效 -
补丁包安全性,对补丁包进行签名校验,这个时候防止补丁包被篡改,实际上虚拟机执行的是
odex而不是dex,还需要进行odex文件进行校验进行MD5完整性校验,如果匹配,则直接加载,如果不匹配,则重新生成odex文件,防止odex文件被篡改