转载请注明出处:juejin.im/post/5a4462…java
从15年开始各技术大佬们开始研究热修复技术,并陆续开源了许多的热修复框架。如 Jasonross 的 Nuwa,美团的 Robust,阿里的 Andfix,腾讯的 Tinker 等等...均是Android 前辈们夜以继日的成果。而如今热修复被普遍地应用于Android 应用和游戏,运用并理解热修复框架在面试中也是加分项。因此,赶忙学起来吧...git
本文以Tinker 做为学习对象,主要讲述各开源框架的对比和记录Tinker 的Demo 集成过程。github
这一节篇幅较长,主要是用本身的话来总结各热修复框架的实现原理。若是想看Tinker接入实现的同窗可进入下一章节。Android 热修复 - Tinker 实现及踩过的坑面试
最先看到热修复框架的相关文章就是Qzone官方的文章,可是Qzone热修复技术的实现代码并无开源。不过GitHub上有一开源的热修复框架Nuwa,实现原理和其类似。这里咱们以Qzone为例进行分析。算法
Qzone的实现原理是生成差分dex文件,将patch.dex插到dexElements的最前面。但patch.dex中的类patchA.java和classes.dex中的类classesB.java会相互引用,若是这两个类所在的patch.dex和classes.dex不是同一个文件就会报错。bash
疑惑的Qzone技术大佬发现classes.dex和classes2.dex也不是同一个文件,为啥不报错?因而继续查,发现了这个判断框架
if(!fromUnverifiedConstant && IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED)){
ide
若是被引用者(也就是classesB.java)这个类被打上了CLASS_ISPREVERIFIED标志,那么就会进行dex的校验。校验不过就报错了。函数
那么这个标志是何时被打上去的? 在apk安装的时候,虚拟机会将dex优化成odex后才拿去执行。在这个过程当中会对全部class进行校验。post
怎么校验的? 假设classesB.java类在它的static方法,private方法,构造函数,override方法中直接引用到classesC.java类。若是classesB.java类和classesC.java类在同一个dex中,那么classesB.java类就会被打上CLASS_ISPREVERIFIED标记,被打上这个标记的类不能引用其余dex中的类,不然就会报错。因此要防止类被打上CLASS_ISPREVERIFIED的标志。
// 校验引用者 ClassB 和被引用者 PatchA 的 dex 是否相同,不一样则报错
if(referrer->pDvmDex != resClassCheck->pDvmDex &&
resClassCheck->classLoader != NULL)
复制代码
如何防止类被打上CLASS_ISPREVERIFIED的标志? Common类会被打包成单独的hack.dex,这样当安装apk的时候,classes.dex内的类都会引用一个在不相同dex中的Common类,这样就防止了类被打上CLASS_ISPREVERIFIED的标志了,只要没被打上这个标志的类均可以进行打补丁操做。
优势:
缺点:
Robust 插件对每一个产品代码的每一个函数都在编译打包阶段自动的插入了一段代码。经过判断 if(changeQuickRedirect != null)
来肯定是否进行热修复,当 changeQuickRedirect 不为 null 时,调用 patch.dex 中同名类的同名方法达到 hotfix 的目的。
如何调用呢? 生成的patch.dex 中有两个主要类,PatchesInfoImpl.java 和修复后的同名类 APatch.java。客户端拿到patch.dex 后,用DexClassLoader 加载patch.dex,反射拿到PatchesInfoImpl.java 这个 class。并建立这个class 的一个对象。而后经过这个对象知道被替换的是谁,给它的变量changeQuickRedirect 赋值为 patch.dex 中的 APatch 的对象,这样就会去执行补丁包中的方法了。
大体流程图以下所示:A_old 表示未被修复的原有类,A_new 表示已修复的新类。
优势
缺点
Andfix 采用的方法是,在已经加载了的类中直接在 native 层替换掉原有方法,是在原来类的基础上进行修改的。其核心在于 replaceMethod 函数,因此只支持方法替换,对于方法的增删,资源更新,so 文件更新及类和属性的替换等都是不支持的。
Andfix 的实现偏 native 层,笔者能力有限,其具体实现过程,就不妄加总结了。 更多实现细节请看 Andfix 官网文章
优势
缺点
在 App 运行到一半的时候,全部须要发生变动的 Class 已经被加载过了,在Android 上是没法对一个 Class 进行卸载的。而 Tinker 的方案,都是让 Classloader 去加载新的类。若是不重启,原来的类还在虚拟机中,就没法加载新类。所以,只有在下次重启的时候,在还没走到业务逻辑以前抢先加载补丁中的新类,这样后续访问这个类时,就会 Resolve 为新的类。从而达到热修复的目的。
Tinker 的实现过程更像是在 Qzone 热修复方案上作优化。核心点是性能最优,消耗最低。
经 Tinker 开发人员调研,Qzone 的方案最大挑战在于性能,即Dalvik平台存在插桩致使的性能损耗,Art平台因为地址偏移问题致使补丁包可能过大的问题。为了不这两个问题,根据 Instant Run 的全量替换新的 Dex 的思路,因而决定将新旧两个Dex 的差别放到补丁包中。
通过调研,BsDiff 算法对 Dex 支持效果不太好,因此,Tinker 开发团队人员自研了 DexDiff 算法。
最终, BsDiff 加载 so 和部分资源文件,DexDiff 加载 Dex文件,以达到性能最优。可是这个方案也有缺点,就是占用 ROM 较大。好吧!如今手机内存都不小,多几十 M 能够接受。
优势
缺点
Type | Nuwa | Robust | Andfix | Tinker |
---|---|---|---|---|
Company | Null | Meituan | Alibaba | Tencent |
开发时间 | 2015 | 2016 | 2015 | 2016 |
替换类 | √ | X | X | √ |
替换So | X | X | X | √ |
替换资源 | √ | X | X | √ |
即时生效 | X | √ | √ | X |
成功率 | 较高 | 最高 | 通常 | 较高 |
接口文档 | ★★ | ★★ | ★★ | ★★★★ |
单就热修复线上 APP 某一处或多处 bug 来讲,Andfix能作到即时修复,且操做简单,不用生成较多的 patch.dex 包,能轻松解决紧急问题。
但对于如非紧急 bug 的修复及小版本的发布,对即时生效性要求不高的状况,Tinker 支持的替换内容较丰富,更胜一筹。
阿里将 Andfix 升级为商业版 SDK Sophix;腾讯将 Tinker 升级为 Bugly。 Sophix 不但支持即时修复,还支持再次启动修复类、so 文件、资源等。做为商用 APP 集成 Sophix 是很好的选择。 但 Tinker 的开源,为其带来了大量的使用者和测试者,除此之外还与各大手机厂商创建联系,使得各厂商在系统定制时也会考虑是否影响热修复的问题。因此 Tinker 的兼容性可见一斑。
总的来讲,各有千秋,各需所需吧。 咱们这里以 Tinker 为学习对象,接下来先让 Tinker-Demo 跑起来,看一下实际效果。 Android 热修复 - Tinker 实现及踩过的坑
推荐阅读:Android 热修复 - Tinker 实现及踩过的坑
Amigo学习(一)解决使用中遇到的问题
记录在此,仅为学习! 感谢您的阅读!欢迎指正! 欢迎加入 Android 技术交流群,群号:155495090。