热修复是在Android上一种越来越流行的技术,需要对底层原理有比较清晰的了解。今天看下几种主流热修复方案分析:
-
为什么使用热修复
-
使用热修复后
-
热修复的流派
-
主流的热修复方案分析
-
对比分析
-
重新发布版本代价太大
-
用户下载安装成本太高
-
BUG修复不及时,用户体验太差
-
无需重新发版,实时高效热修复
-
用户无感知修复,无需下载新的应用,代价小
-
修复成功率高,把损失降到最低
1、底层替换方案 底层替换方案限制颇多,但时效性最好,加载轻快,立即见效。 代表:支付宝的Andfix 2、类加载方案 类加载方案时效性差,需要重新冷启动才能见效,但修复范围广,限制少。 百度实现的HotFix、QQ空间补丁技术、Qfix方案、微信的Tinker方案 3、融合方案 两种方案的结合,优势互补。 Sophix
AndfixAndFix的思想是直接更改修复的方法,就是找到这个类中需要修复的函数然后调用replaceMethod方法。在Native层使用指针替换的方式替换bug方法,以达到修复bug的目的。
优点:
-
BUG修复的即时性
-
补丁包同样采用差量技术,生成的PATCH体积小
-
对应用无侵入,几乎无性能损耗
局限性:
-
只能基于方法修复,而且对平台的兼容性不佳,
-
不支持新增字段,以及修改
方法,也不支持对资源的替换。
超级补丁技术原理是基于Android Dex分包方案的,而Dex分包方案的关键就是Android的ClassLoader体系。 关键点:DexClassLoader可以用来执行没有安装的程序代码
除了类加载问题之外还存在CLASS_ISPREVERIFIED标记的问题: 只要在static方法,构造方法,private方法,override方法中直接引用了其他dex中的类,那么这个类就不会被打上CLASS_ISPREVERIFIED标记。
解决方案:在所有类的构造函数中插入这行代码 System.out.println(AntilazyLoad.class);
超级补丁技术原理是基于Android Dex分包方案的,而Dex分包方案的关键就是Android的ClassLoader体系。把多个dex文件塞入到app的classloader之中android加载的时候,如果有多个dex文件中有相同的类,就会加载前面的类,原理就是把有问题的类替换掉,把需要的类放到最前面,达到热补的目的。
优势:
-
没有合成整包(和微信Tinker比起来),产物比较小,比较灵活
-
可以实现类替换,兼容性高
不足:
-
不支持即时生效,必须通过重启才能生效。
-
在ART模式下,如果类修改了结构,就会出现内存错乱的问题。为了解决这个问题,就必须把所有相关的调用类、父类子类等等全部加载到patch.dex中,导致补丁包异常的大,进一步增加应用启动加载的时候,耗时更加严重。
-
Tinker也是Dex加载原理, 不同是,Tinker 下发新旧DEX的差异包,优化了DexDiff算法实现dexPatch体积更小。然后将差异包和旧包合成新dex之后进行dex的全量替换,避免了QQ补丁中的插桩操作。
-
全量合成新dex,消除重复class重复带来的冲突
优势:
-
合成整包,不用在构造函数插入代码。
-
性能提高。兼容性和稳定性比较高。
-
开发者透明,不需要对包进行额外处理。
不足:
-
不支持即时生效,必须通过重启应用的方式才能生效。
-
开启新的进程才能进行合并,并且很容易因为内存消耗等原因合并失败。
-
合并时占用额外磁盘空间,对于多DEX的应用来说,如果修改了多个DEX文件,就需要下发多个patch.dex与对应的classes.dex进行合并操作时这种情况会更严重,因此合并过程的失败率也会更高。
Sophix的代码修复同时涵盖底层替换方案和类加载方案。在补丁生成阶段,补丁工具会根据实际代码变动情况进行自动选择,针对小修改,在底层替换方案限制范围内的,就直接采用底层替换修复方案,而对于代码修改超出底层替换限制的,会使用类加载替换,此时及时性没那么好。 另外,运行时阶段,Sophix还会再判断所运行的机型是否支持热修复,这样即使补丁支持热修复,但由于机型底层虚拟机构造不支持,还是会走类加载修复,从而达到最好的兼容性。
Sophix资源修复方案,优越性超过了Google官方的Instant Run方案。整个资源替换的方案优势在于:
-
不修改AssetManager的引用处,替换更快更完全。(对比Instanat Run以及所有copycat的实现)
-
不必下发完整包,补丁包中只包含有变动的资源。(对比Instanat Run、Amigo等方式的实现)
-
不需要在运行时合成完整包。不占用运行时计算和内存资源。(对比Tinker的实现)
So修复:类似类修复反射注入方式。把补丁so库的路径插入到nativeLibraryDirectories数组的最前面,就能够达到加载so库的时候是补丁so库,而不是原来so库的目录,从而达到修复的目的。采用这种方案,完全由Sophix在启动期间反射注入patch中的so库
优点: 1、Sophix集成简单,不需要配置繁琐的各种参数。 2、Sophix支持即时生效(事实上我第一次运行first版本后,直接就弹出了toast,而此时后台数据显示设备加载成功数为1,设备推送成功数为0)。 3、Sophix支持run instant,而tinker不支持。编译中不支持run instant,速度大大降低! 4、Sophix的补丁是基于阿里自身的SophixPatchTool打包生成,不是在AS中生成的,有种解耦的感觉,而且不需要备份太多的版本。
缺点: 处于公测阶段,可能面临收费
热修复框架对比
参考资料:
https://github.com/alibaba/AndFix https://github.com/dodola/HotFix https://github.com/Tencent/tinker https://github.com/jasonross/Nuwa https://github.com/bunnyblue/DroidFix http://blog.csdn.net/u013795543/article/details/73250166