出自:http://www.cnblogs.com/Creator/p/7007694.htmlhtml
为何要作热更新java
当一个App发布以后,忽然发现了一个严重bug须要进行紧急修复,这时候公司各方就会忙得焦头烂额:从新打包App、测试、向各个应用市场和渠道换包、提示用户升级、用户下载、覆盖安装。android
重点是还会有原来的版本遗留,不管你怎么提示都有人放弃治疗,不肯意升级,强制不能使用体验又足够糟糕到让人不能启齿。git
若是这是一个影响公司收入或者体验影响极其很差的Bug,那完蛋了,可能公司老板会对整个技术团队的技术能力丧失信心,其对技术人员的伤害是致命的。github
最后最致命的是:编程
有时候仅仅是由于不当心写错了一行代码,就让全部的加班都付之东流,苦不苦,冤不冤,想一想都苦。数组
还有一种剧情是研发总监把锅甩给测试团队,测试不过关,测试摊摊手说我也不是神啊,总会有漏网之鱼.服务器
那能不能神不知鬼不觉再没有产生较大影响前把bug快速修复了呢?微信
热更新的行业状况app
先来讲说Android
并非由于Android更有料就先说他,而是它的用户量级比Iphone大,咱们写文章也是讲究大数据分析的不是..
Andoid端在15年热补丁就比较火,前后出现了Dexposed、AndFix,Qzone超级补丁的类Nuwa方式,微信的Tinker, 大众点评的nuwa、百度金融的rocooFix, 饿了么的amigo以及美团的robust.
再来看看Iphone端
技术上要在 iOS 上作到原生动态化比 Android 更容易,iOS 开发语言 Objective-C 天生动态,运行时都能随意替换方法,运行时加载动态库又是项很老的技术,只要我把增量的代码和资源打包到一个 framework 里,动态下发运行时加载,修 bug,加功能都不在话下,性能彻底无损,这件事就结束了。
可是呢。苹果把加载动态库的功能给封了,动态库必须跟随安装包一块儿签名才能被加载,没法经过别的途径签名后再下发。
因而有了 waxPatch 和 JSPatch 这样的方案,以及异军突起不局限于热修复Bug而能作主体功能发布的React Native 和 Weex,后面又有了吊口味的滴滴的DynamicCocoa方案和OCScript
热更新的技术原理
先来讲JAVA
技术派系:
• Native,表明有阿里的Dexposed、AndFix与腾讯的内部方案KKFix;
• Java,表明有Qzone的超级补丁、大众点评的nuwa、百度金融的rocooFix, 饿了么的amigo以及美团的robust。
Native流派与Java流派都有着本身的优缺点,它们具体差别你们可参考上文。事实上历来都没有最好的方案,只有最适合本身的。
下面咱们来一一简单看下各热更新的实现方案:
Dexposed
阿里开源项目,基于Xposed的AOP框架,方法级粒度,能够进行AOP编程、插桩、热补丁、SDK hook等功能。
不一样的是,Xposed经过劫持 zygote(须root),而dexposed经过劫持 java method ( 而非楼上说的劫持class loader方法),将java method改变为native,而且将这个方法的实现连接到一个通用的Native Dispatch方法上.)用处,最大的天然是hotpatch,用这种东西来热替换某个致使崩溃的方法。手淘还有作的一件事,就是用它做性能监控。这主要得益于无侵入式的方法调用Befor和After事件,可以让咱们很好的记录和分析一个方法的调用时间。开源项目promeG/XLog就是基于dexposed实现的方法调用logging
使用方法:
dexposed提供了3个使用方法:
beforeHookedMethod
afterHookedMethod
replaceHookedMethod
来看看使用方式,也极其简单.
优缺点:
来讲说硬伤吧,不支持art,不支持art,不支持art。
不支持Dalvik 3.0.
因此注定它会逐步失声,再多的优势也是徒劳
插播一条硬广: 技术文章转发收录太多,此文出处 http://www.cnblogs.com/Creator/ 以及微信公众号: 互联网手艺人
Qzon的超级补丁方案
该方案基于的是android dex分包方案的,关于dex分包方案自己更可能是为了解决Android的64K方法调用限制问题,具体的缘由是:
• DexOpt 会把每个类的方法 id 检索起来,存在一个链表结构里面,可是这个链表的长度是用一个 short 类型来保存的,致使了方法 id 的数目不可以超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。
•Dexopt 使用 LinearAlloc 来存储应用的方法信息。Dalvik LinearAlloc 是一个固定大小的缓冲区。在Android 版本的历史上,LinearAlloc 分别经历了4M/5M/8M/16M限制。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提升到了8MB 或16MB。当方法数量过多致使超出缓冲区大小时,也会形成dexopt崩溃
尽管在新版本的 Android 系统中,DexOpt 修复了方法数65K的限制问题,而且扩大了 LinearAlloc 限制,可是这套技术机制保留了下来
分包的方案简单来讲就是在打包时将应用的代码分红多个 dex,使得主 dex 的方法数和所需的 LinearAlloc 不超过系统限制。在应用启动或运行过程当中,首先是主 dex 启动运行后,再加载从 dex,这样就绕开了这两个限制。
如何拆分和如何加载能够查看Google官方的方案MultiDex
http://developer.android.com/intl/zh-cn/tools/building/multidex.htm
Qzon的超级补丁方案玩的是什么招呢?
把BUG方法修复之后,放到一个单独的DEX里,插入到dexElements数组的最前面,让虚拟机去加载修复完后的方法。
Patch.dex中的A.class会有优先加载,后续的dex中的A.class就不会加载直接跳过,达到修复目的。
核心问题:
当两个调用关系的类不在同一个DEX时,就会产生异常报错。咱们知道,在APK安装时,虚拟机须要将classes.dex优化成odex文件,而后才会执行。在这个过程当中,会进行类的verify操做,若是调用关系的类都在同一个DEX中的话就会被打上CLASS_ISPREVERIFIED的标志,而后才会写入odex文件。具体如何解决这个问题能够参见QQ空间终端开发团队QQ空间终端开发团队发布的” 安卓App热补丁动态修复技术介绍”
优缺点:
1.没有合成整包(和微信Tinker比起来),产物比较小,比较灵活
2.能够实现类替换,兼容性高。(某些三星手机不起做用)
不足:
1.不支持即时生效,必须经过重启才能生效。
2.为了实现修复这个过程,必须在应用中加入两个dex!dalvikhack.dex中只有一个类,对性能影响不大,可是对于patch.dex来讲,修复的类到了必定数量,就须要花很多的时间加载。对手淘这种航母级应用来讲,启动耗时增长2s以上是不可以接受的事。
3.在ART模式下,若是类修改告终构,就会出现内存错乱的问题。为了解决这个问题,就必须把全部相关的调用类、父类子类等等所有加载到patch.dex中,致使补丁包异常的大,进一步增长应用启动加载的时候,耗时更加严重。
微信Tinker
根据微信内部人士介绍:微信tinker项目之初最大难点在于如何突破Qzone方案的性能问题,经过研究Instant Run的冷插拔与buck的exopackage给了咱们灵感。它们的思想都是全量替换新的Dex
由于使用全新的dex,因此天然绕开了Art地址可能错乱的问题,在Dalvik模式下也不须要插桩,加载全新的合成dex便可。
焦点问题是合并的过程会不会有问题,会不会耗时或者效率低? 为此腾讯在DEX方面也花了不少时间研究内部的格式以及如何作Merge和进行校验工做,详细了解能够查看” 大腾讯的第一个开源项目「Tinker」”这篇文章
优点:
1. 合成整包,不用在构造函数插入代码,防止verify,verify和opt在编译期间就已经完成,不会在运行期间进行
2. 性能提升。兼容性和稳定性比较高。
3. 开发者透明,不须要对包进行额外处理。
不足:
1. 与超级补丁技术同样,不支持即时生效,必须经过重启应用的方式才能生效。
2. 须要给应用开启新的进程才能进行合并,而且很容易由于内存消耗等缘由合并失败。
3. 合并时占用额外磁盘空间,对于多DEX的应用来讲,若是修改了多个DEX文件,就须要下发多个patch.dex与对应的classes.dex进行合并操做时这种状况会更严重,所以合并过程的失败率也会更高。
阿里Andfix方案
为什么惟独Andfix可以作到即时生效呢?
缘由是这样的,在app运行到一半的时候,全部须要发生变动的Class已经被加载过了,在Android上是没法对一个Class进行卸载的。而腾讯系的方案,都是让Classloader去加载新的类。若是不重启,原来的类还在虚拟机中,就没法加载新类。所以,只有在下次重启的时候,在还没走到业务逻辑以前抢先加载补丁中的新类,这样后续访问这个类时,就会Resolve为新的类。从而达到热修复的目的。
Andfix采用的方法是,在已经加载了的类中直接在native层替换掉原有方法,是在原来类的基础上进行修改的。
以Art为例,每个Java方法在art中都对应着一个ArtMethod,ArtMethod记录了这个Java方法的全部信息,包括所属类、访问权限、代码执行地址等等。经过env->FromReflectedMethod,能够由Method对象获得这个方法对应的ArtMethod的真正起始地址。而后就能够把它强转为ArtMethod指针,从而对其全部成员进行修改。
这很C/C++ 研发的味道,实际上Andfix的核心代码replaceMethod就是用cpp写的。
面临的挑战:
由于安卓各ROM乱象的缘由,ArtMethod的结构可能会不同, ArtMethod类包含些什么其实都是在编译阶段,在运行阶段可能不是这么回事,例如sizeof(ArtMethod)可能实际在各平台就彻底不同,可是咱们在编译的时候就肯定了值,直接操做容易改乱内存数据致使奔溃。
有什么好的方法来解决这个问题呢?
来看看奇技淫巧
因为f1和f2都是static方法,因此都属于direct ArtMethod Array。因为NativeStructsModel类中只存在这两个方法,所以它们确定是相邻的。
那么咱们就能够在JNI层取得它们地址的差值:
而后,就以这个methSize做为sizeof(ArtMethod),代入以前的代码。
问题就迎刃而解了。即便之后的Android版本不断修改ArtMethod的成员,只要保证ArtMethod数组还是以线性结构排列就能完美兼容。
著:此方法最新方案并不在开源的方案中
最大的优点在于
1. BUG修复的即时性
2. 补丁包一样采用差量技术,生成的PATCH体积小
3. 对应用无侵入,几乎无性能损耗
不足:
1. 不支持新增字段,以及修改<init>方法,也不支持对资源的替换。
再来看看IOS的热更新技术:
苹果把加载动态库的功能给封了,动态库必须跟随安装包一块儿签名才能被加载,没法经过别的途径签名后再下发。
Wax
最先要从 Wax 这个项目开始说,你们都知道 Objective-C 有着很是强大的动态特性。好比说:
•运行时构造类和方法
•运行时替换方法的实现实际上这两个能力是很是恐怖的像脚本语言那样,文本即代码,无须编译。后来出现了一个叫作 Wax的项目(这个项目目前由阿里巴巴维护),这个项目打出的口号是用 Lua 来写 iOS 原生应用,固然现实中没有人会这样干,由于写起来实在是太痛苦了。可是鉴于 iOS 应用审核比写 Wax 还痛苦,因此 Wax 成为了作 HotFix 的最佳选择。
这个项目的作法是经过加载 Lua 脚本,动态的生成 Objective-C 的方法,一般用来替换掉出了问题的那个,Lua 脚本是能够动态下发的,因此也就实现了修复线上 bug 的使命。
固然,Wax 用起来是极为痛苦的,尤为是和 Objective-C 的类型转换。
JSPatch
iOS 7 的时候 Apple 推出了 JavaScriptCore,这是一个很是有趣的框架,他是 JS 与原生交互的桥梁,让你在原生和 JS 之间穿梭自如,如今 iOS 平台各类动态技术大多都是基于此。
JSCore 推出不久以后,一个更优秀的项目诞生了:由 bang 写的 JSPatch。这个项目无疑从各类角度碾压了 Wax,而且 JS 也比 Lua 更为人熟知,因此也就迅速替代 Wax 成为了热修复的主流选择。
JSPatch 的接入成本很是低,对项目的影响也很是小,不须要引入额外的脚本解释器(由于已经有 JSCore 了),而且 JS 写起来真的比 Lua 要爽不少。
3月8日,不少iOS开发者发了警告邮件,声称其App违规使用动态方法,责令限时整改,Jspatch一直就被打入冷宫了
此次警告事件无疑是对iOS平台Native动态化是一次严重打击,其影响甚至可能波及到Android平台,毕竟Google也是禁止加载远程代码的,而且执行更为严格,只是管不到中国的Android开发而已。
滴滴的DynamicCocoa
DynamicCocoa这种方案,绕了一个更大的道,从编译阶段入手,经过 clang 把 OC 代码编译成本身定制的 JS 格式,再动态下发去执行,作到原生开发,动态运行,主打动态添加功能,固然顺便把修 bug 也给支持了。手机 QQ 内部也有一个相似的方案,不过更进一步,他们经过 clang 把 OC 代码编译成本身定制的字节码动态下发,而后开发一个虚拟机去执行(惊呆了),一样实现了原生开发,动态运行,都是 NB 得很的方案。只要底层处理作得足够好,也是个成本低收益高的方案,不过目前都还没开源,在github上是一个只有两行README可是有1000+Star的神奇项目
DynamicCocoa与Jspatch 思路上都是实现 JS 和 OC 的互调:DynamicCocoa 的重点是动态化能力,优点在于彻底不用写 JS 和更多的语法特性支持;对于 HotPatch 来讲 JSPatch 是更加小巧、轻量的解决方案。
听说在滴滴 App 已经上线并使用了好几个版本,如滴滴小巴、专车接送机都有过 10k 级别的动态化模块上线。
20170612 苹果已经正式禁止热更新,给涉及到检测出来的开发者发了邮件,同时提供 App Store “自动更新的分阶段发布” 功能。
苹果是如何检测的呢,大概能够从给开发者的邮件看出来:
最后咱们来看看苹果的灰度发布功能吧,对于一个花了将近3年时间作国内超大规模私有云的我来讲,感觉到了熟悉的味道(服务器端灰度发布也是一个套路)