Android 几种流行热修复方案整理

来源:互联网 发布:淘宝anna家的创始人 编辑:程序博客网 时间:2024/05/29 19:56
Android 热修复
Dexposed
基于Xposed的AOP框架,方法级粒度,可以进行AOP编程、插桩、热补丁、SDK hook等功能。
Xposed需要Root权限,是因为它要修改其他应用、系统的行为,而对单个应用来说,其实不需要root。 Xposed通过修改Android Dalvik运行时的Zygote进程,并使用Xposed Bridge来hook方法并注入自己的代码,实现非侵入式的runtime修改。比如蜻蜓fm和喜马拉雅做的事情,其实就很适合这种场景,别人反编译市场下载的代码是看不到patch的行为的。小米(onVmCreated里面还未小米做了资源的处理)也重用了dexposed,去做了很多自定义主题的功能,还有沉浸式状态栏等。
应用启动的时候,都会fork zygote进程,装载class和invoke各种初始化方法,Xposed就是在这个过程中,替换了app_process,hook了各种入口级方法(比如handleBindApplication、ServerThread、ActivityThread、ApplicationPackageManager的getResourcesForApplication等),加载XposedBridge.jar提供动态hook基础。
Method方法级的替换是指,可以在方法前、方法后插入代码,或者直接替换方法。只能针对java方法做拦截,不支持C的方法。
缺点: 1.不支持art;
2.线上release版本进行混淆后,写patch很复杂(反射+内部类,可能还有包名和内部类的名字冲突)。
【淘宝】https://github.com/alibaba/dexposed

AndFix
采用native hook的方式,以Field为切入点,直接使用dalvik_replaceMethod替换class中方法的实现。由于它并没有整体替换class, 而field在class中的相对地址在class加载时已确定,所以AndFix无法支持新增或者删除filed的情况(通过替换init与clinit只可以修改field的数值)。
在dalvik上的实现略有不同,是通过jni bridge来指向补丁的方法。
缺点:支持的补丁场景相对有限,仅仅可以使用它来修复特定问题(兼容性较差)
【支付宝】https://github.com/alibaba/AndFix

ClassLoader
基于的是android dex分包方案(multidex)实现。把多个dex放进app的classloader之中,从而使得所有dex的类都能被找到,在findClass的过程中,如果出现了重复的类,会使用第一个找到的类。
该热补丁方案:把有问题的类打包到一个dex(patch.dex)中去,通过反射插入到dexElements数组的最前面。在实践中,会发现运行加载类的时候报preverified错误,原来在DexPrepare.cpp,将dex转化成odex的过程中(当一个apk在安装的时候,apk中的classes.dex会被虚拟机(dexopt)优化成odex文件,然后才会拿去执行),会在DexVerify.cpp进行校验,验证如果直接引用到的类和clazz是否在同一个dex,如果是,则会打上CLASS_ISPREVERIFIED标志。通过在所有类(Application除外,当时还没加载自定义类的代码)的构造函数插入一个对在单独的dex的类的引用(之所以选择构造函数是因为他不增加方法数,一个类即使没有显式的构造函数,也会有一个隐式的默认构造函数。),就可以解决这个问题。空间使用了javaassist进行编译时字节码插入。
缺点:下一次启动才生效;补丁包大小;
Dalvik:对启动速度略微有影响(插桩导致对程序运行时的性能产生影响),
Art下:补丁中的类出现修改类变量或者方法,可能会导致出现内存地址错乱的问题。
【QQ空间】https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a&scene=1&srcid=1106Imu9ZgwybID13e7y2nEi#wechat_redirect
https://github.com/jasonross/Nuwa
https://github.com/dodola/HotFix
https://github.com/bunnyblue/DroidFix

Tinker
思想:将新旧两个Dex的差异放到patch补丁包中,采用BsDiff算法(微信研发),全量替换新的Dex。
缺点:占用Rom体积;这边大约是你所修改Dex大小的1.5倍(Dex压缩成jar的大小加上生成的dexopt文件大小);
一个额外的合成过程;虽然单独放在一个进程上处理,但是合成时间的长短与内存消耗也会影响最终的成功率(与修改Dex大小、补丁大小相关)。
【微信】http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=2649286306&idx=1&sn=d6b2865e033a99de60b2d4314c6e0a25&scene=23&srcid=0705vd1zLzQEHZ9G6JyQSqTG#rd

比较
Dexposed不支持Art模式(5.0+),且写补丁有点困难,需要反射写混淆后的代码,粒度太细,要替换的方法多的话,工作量会比较大。

AndFix支持2.3-6.0,但是不清楚是否有一些机型的坑在里面,毕竟jni层不像java曾一样标准,从实现来说,方法类似Dexposed,都是通过jni来替换方法,但是实现上更简洁直接,应用patch不需要重启。但由于从实现上直接跳过了类初始化,设置为初始化完毕,所以像是静态函数、静态成员、构造函数都会出现问题,复杂点的类Class.forname很可能直接就会挂掉。

ClassLoader方案支持2.3-6.0,会对启动速度略微有影响,只能在下一次应用启动时生效,在空间中已经有了较长时间的线上应用,如果可以接受在下次启动才应用补丁,是很好的选择。

Tinker在ART模式下的占用Rom空间大,合成过程中,消耗时间与内存。

总的来说,在兼容性稳定性上,ClassLoader方案较可靠

补充:
ART(Android Runtime)是Android在4.4版本中引入的新虚拟机环境,在5.0版本正式取代了Dalvik VM。ART环境下,App安装时其包含的Dex文件将被dex2oat预编译成目标平台的机器码,从而提高了App的运行效率。在这个预编译过程中,dex2oat对目标代码的优化过程与Dalvik VM下的dexopt有较大区别,尤其是在5.0版本以后ART环境下新增的方法内联优化。
方法内联改变了原本的方法分布和调用流程,对热修复方案势必会带来影响。
显然,对Native派而言(Dexposed,Andfix),如果修改的方法被内联到了调用者的代码里,则修改将不会生效,因为内联的代码不再需要方法调用,也就不会涉及到额外的Native层方法描述结构体。
对于Java派而言(ClassLoader),插入一段逻辑的做法基本不受影响,因为内联时会将被修改的方法连同插入的那段逻辑一起复制到调用者的代码里,结果和内联之前是等价的。
方法内联之所以会导致优先加载补丁Dex的方案出现上述问题,本质上是因为补丁Dex只覆盖了旧Dex里的一部分类,一旦被覆盖的类的方法被内联到了调用者里,则加载类的过程还是正常的,即从补丁Dex里加载了新版本的类。但由于内联,执行流程并未跳转到新的方法里,于是所有关于新版本的类的方法、成员、字符串的查找用的就都是旧方法里的索引了。
http://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=2649286426&idx=1&sn=eb75349c0c3663f10fbdd74ef87be338&chksm=8334c398b4434a8e6933ddb4fda4a4f06c729c7d2ffef37e4598cb90f4602f5310486b7f95ff&scene=0#rd


参考资料: http://blog.zhaiyifan.cn/2015/11/20/HotPatchCompare/

0 0
原创粉丝点击