Android热修复学习(三)微信热修复 tinker

来源:互联网 发布:c语言打印等腰杨辉三角 编辑:程序博客网 时间:2024/06/05 01:55

本来准备在后续的文章中,陆续写下自己对现有的热修复框架(主要为Qzone团队方案和阿里的AndFix)原理的理解,以方便自己日后查看,不过今天看到新的文章微信Android热补丁实践演进之路,感觉微信团队的技术真是强大,研究的很深入透彻,对之前的方案又有了新的认识。下面只是简要的记下自己的一些认知吧。

  • QZone方案 
    QQ空间团队的方案github上有很多扩展的项目,不过大致原理应该是类似的。在(二)中我们看到multidex是利用了DexPathList的Element[],通过反射在把映射了其它dex的Element和之前的数组合并到新的数组,然后反射设置回DexPathList,从而达到动态加载多个dex的目的。

    而QQ空间团队不光是改变了Element[]的大小,而且还把后来增加的插入到数组的最前面,这样就会先加载后来增加的dex。如果增加的Element里面有之前Element包含的类,则会替换掉之前Element里相同的类,因为的DexPathList的findClass()方法在前面的Element成功加载类后,就直接返回了,后面的Element根本没机会去加载这个类,这个在(一)中有讲过。

    这个原理很简单,但是实践中总会出现很多没有预期的问题。apk安装的时候apk中的classes.dex会被虚拟机(dexopt)优化成odex文件,在这个过程中,一个类一些 方法中直接引用到的类和这个类都在一个dex文件中,会被打上CLASS_ISPREVERIFIED标签。然后在运行加载类的过程中,有CLASS_ISPREVERIFIED标签的类会校验直接引用到的类是否在同一个dex文件,如果两个相关联的类在又不同的dex中就会报错。而上面新增加的dex中要替换的类,和之前的类可能都没在一个dex,而在安装的dexopt过程中有的类又可能因为要替换的这个类和它在同一个dex中而被打上CLASS_ISPREVERIFIED标签,这样运行中就会报错了。

    QQ空间团队解决办法为:在所有类的构造函数中引用一个打在单独dex中的类,这样就不会被打上CLASS_ISPREVERIFIED标签。程序启动的时候先加载这个类所在这个的dex。但是有一个类的构造函数中不能引用,这个类就是Application类的子类,因为Application会最先加载。选择构造方法是因为不增加方法数,空间使用的是在字节码插入代码,而不是源代码插入,使用的是javaassist库来进行字节码插入的。

    从微信的文章可以知道,该方案简单,成功率高,但是在dalvik上对性能有影响。而在art中虽然对效率没什么影响,但是如果一个类修改了类结构,之前引用它的类调用它的时候可能出现地址错乱,则需要把相关联的类都放到新的dex中,增加了补丁dex的大小。需要重启后才能生效,替换的维度是类Class。

  • 阿里的AndFix 
    AndFix则没有利用Classloader,而是在native层,通过替换方法地址来实现的。在native层通过针对不同虚拟机(dalvik和art)做了不同的处理。之前通过看源码dalvik是通过把替换的方法修改为native方法,并指向一个固定的native函数,在这个函数中根据要替换的方法再进行不同的转发调用新的java方法,不过现在已经更新了代码,更改了实现方式。art中则好像直接进行了地址替换。

    从上面可以看出AndFix的替换维度是方法层的,那是怎么把新旧方法做关联的呢,AndFix用自己的差分工具apkpatch,对新旧apk差分出一个patch,patch中有新的方法的dex,并且新的方法有Annotation标识要替换之前的哪个类的哪个方法,所以这样就知道了关联关系,然后再native层进行替换。

    可以看到AndFix对Classloader加载apk的dex文件没有任何干预,所以不需要重启即可生效,只是替换了方法。不过这也导致已经加载的Classz的字段已经固定,无法支持新增或者删除filed的情况。而且由于在native层做改变,可能兼容性比较差。AndFix的替换维度是方法Method。

  • 微信的新方案Tinker 
    微信团队对现有的方案优缺点进行了深入的分析和总结,由于AndFix的局限性和兼容性差原因放弃了这种方案,而对QZone方案通过研究Instant Run的冷插拔与buck的exopackage带来的灵感进行了升级。QZone方案只是插入了Elements(补丁dex),而Tinker则是替换了整个Element数组(所有dex)。替换是通过patch和apk原始的dex进行合并,重启后加载合并后的新dex,这样基本上可以认为没有对dexopt和Classloader加载dex文件的过程进行干预,所以QZone方案的两个问题也就不复存在。

    可以看到微信团队的新方案Tinker通过替换整个dex,完美了规避了现有方案的一些问题。除了合并新dex稍长时间与内存消耗,以及第一次重启加载整个新dex时dexopt的时间,似乎没有其它的大问题,还是比较完美的。希望微信团队能够早日开源Tinker,以及配套的对patch进行版本管理线上监控的系统方案。

    下面借用微信文章的一张图,来说明各个热修复方案的优缺点对比,说明它们各自的使用场景: 

    这里写图片描述


微信热修复: https://github.com/Tencent/tinker




0 0
原创粉丝点击