阿里HotFix2.0升级详解 畅谈热修复领域那些事

简介: 本文所介绍的阿里Hotfix 2.x是在1.x版本进行了优化和创新,不仅支持灵活切换热部署和冷部署的方案;同时,实现了资源、SO文件、类修复的实时生效;整体接入过程采用傻瓜式方法,完全不侵入打包过程,为用户提供了可视化的UI界面。

摘要:

热修复领域充斥着各大流派,如阿里AndFix、美团Robust、QQ空间、微信Tinker等,每种方法各有优劣。本文所介绍的阿里Hotfix 2.x是在1.x版本进行了优化和创新,不仅支持灵活切换热部署和冷部署的方案;同时,实现了资源、SO文件、类修复的实时生效;整体接入过程采用傻瓜式方法,完全不侵入打包过程,为用户提供了可视化的UI界面。


在阿里HotFix2.0升级详解直播中,阿里HotFix核心开发工程师悟二从热修复背景、常见的热修复方案、阿里HotFix历程及2.0的突破与创新三个方面展开了详细的演讲。分享中,他重点介绍了阿里Hotfix2.X 类、SO文件、资源文件修复方案以及管理后台服务,并对阿里Hotfix 2.X将来需要新增的功能也做了展望。

热修复背景

image.png

正常Bug修复流畅包括版本上线、用户安装、发现Bug、紧急修复、重新发版、用户安装六步。该流程中存在着明显的不足,首先重新发布版本代价太大,由于APK是通过多渠道发布的,重新发版时,需要再次对每个渠道推送新的APK包;其次,用户下载安装成本也随之增高;此外,由于需要重新发版、安装,导致Bug修复不及时,用户体验较差。

热修复流程的前四步同正常修复流程相同,但在热修复中,不再进行重新发版,取而代之的是生成上传补丁;此后,SDK拉取加载补丁,完成Bug修复。在热修复过程中,无需重新发版,能够实时高效热修复;同时实现了用户无感知修复,无需下载新的应用,代价小。相比于正常修复过程,热修复的成功率更高,将损失降到了最低。

热修复的几大流派

常见的热修复流派包括阿里AndFix、美团Robust、QQ空间、微信Tinker等,下面来一一介绍。

阿里AndFix

image.png

阿里andfix 热修复方案通过Hook本地方法. 并没有整体替换class,整体流程如下:第一步,打开链接库得到操作句柄, 获取native层内部函数, 得到classobject对象;第二步,修复访问权限属性为Public ;第三步,得到新旧方法的指针,新方法指向目标方法,实现方法的替换。

整个过程中不侵入打包,性能无损耗;同时可以即时生效。其缺点同样明显:兼容性方面很不稳定,需要针对Dalvik虚拟机和Art虚拟机做适配;不支持新增类方法/字段,以及修改方法,也不支持对资源的替换;运行时方法被Patch,有Crash风险。

美团Robust

美团热修复方案Robust 的原理类似Instant Run,每个产品代码的每个函数都在编译打包阶段自动的插入了一段代码。客户端拿到Patch.Dex后,用DexClassLoader加载Patch.Dex。其中的changeQuickRedirect字段赋值为用Patch.Dex中的StatePatch.java这个Class New出来的对象。

在整个打Patch过程中,该方案正常的使用DexClassLoader,兼容性高;未反射注入,能够实时生效。该方案的缺点在于:因为在每端函数前插入代码,需要侵入打包过程;原来能被ProGuard内联的函数不能被内联了,所以可能导致方法数的增加,可能会超过65536限制,同时也会导致APK体积增大;该方案不支持SO文件和资源文件的修复。

手机QQ空间

image.png

手机QQ空间热修复方案可以用注入和插桩概括。其大致过程是:把Bug方法修复之后,放到一个单独的DEX内,插入到Dex Elements数组的最前面,让虚拟机去加载修复完后的方法。

该方案类似谷歌的Multidex ,在保障稳定性的前提下兼容性很高。缺点是:不支持实时生效;在Davilk下,类加载存在性能问题;Art下,补丁包涵有类、父类以及引用该类的所有类,因此补丁包较大;由于原DEX中的类需要引用额外的DEX类,需要侵入式打包。

微信Tinker

微信Tinker为了解决QQ空间补丁技术由于插桩带来的效率问题,引入DEX差量包。其主要的原理与QQ空间超级补丁技术基本相同,最大区别在于:不再将Patch.dex增加到Elements数组中,而是差量的方式给出Patch.Dex;然后将Patch.Dex与应用的Classes.Dex合并,然后整体替换掉旧的DEX,达到修复的目的。

该方案中通过自研DexDiff算法,深度利用Dex的格式来减少差异的大小,从而做到补丁包足够小。其缺点在于:不支持实时生效;由于补丁DEX需要和原DEX合并,需要占用额外内存和磁盘空间,并且很容易因为内存消耗等原因合并失败;与QQ空间补丁技术相同,同样需要侵入式打包。

image.png

上图是Hotfix和主流的热修复方案的效果对比。可以看出,在即时生效、性能消耗、Rom体积、接入复杂度、补丁包大小、类替换、SO文件替换、资源方案等方面,Hotfix都具有相对的优势。

阿里Hotfix 1.x版本

image.png

阿里Hotfix 1.x在AndFix的基础上,增加了补丁管理后台;同时基于手淘的实践,针对AndFix做了大量优化, 性能上提高了兼容和稳定性;功能上支持新增类并提供了更小的补丁包(这是因为基于类方法作为粒度)。

从图中可以看到阿里Hotfix 1.x服务后台功能,用户可以新建应用版本,然后根据版本号上传补丁;此外,还提供了补丁控制功能,比如停止发布、继续发布、灰度/全量发布等功能。

但阿里Hotfix 1.x仍存在很多限制:

不支持资源、So文件修复;不支持新增类方法/类字段,这是因为Hotfix 1.x本质上是Hook一个已存在的的方法;

参数包括Long、Double、Float基本类型的方法不能被Patch,同时参数超过8的方法不能被Patch;

被反射调用的方法不能被Patch,具体来说是非静态方法的反射调用会提示IllegalArgumentException 异常,当静态方法被反射调用,如果反射调用不涉及类对象,则可以被Patch;

构造方法不能被Patch,实际上不允许修改一个类字段(包括静态的和非静态的);

正在运行的方法不能被Patch,也就是说如果一个方法正在运行,然后方法的在Native层的结构被替换, 那么就很可能导致Crash。

阿里Hotfix2.0方案

image.png

相比于1.X版本,阿里Hotfix 2.x版本将上面的限制完全取消,不仅仅只基于AndFix,而是灵活切换热部署和冷部署的方案;实现了资源、SO文件、类修复的实时生效,同时采用了傻瓜式接入方案,完全不侵入打包过程,对用户提供了可视化的UI界面打补丁。

阿里Hotfix 2.x版本由于相比于1.x版本,变更很大,,因此需要预先做大规模的集成测试才会正式上线。

阿里Hotfix2.X 类修复方案

Hotfix2.X在热修复过程中是不侵入打包过程的,而是通过补丁工具生成补丁。由于热部署Andfix修复正在运行的方法有Crash的风险, 所以补丁工具提供参数由业务方来决定是否尝试走热部署;如果用户Patch的方法没有被高频调用同时又有实时生效的需求,那么可以优先选择走热部署方案;但这并非绝对,当代码变更导致热部署不支持时,还是会转向冷部署。

热部署

热部署就是AndFix支持的代码变更,此时走优化后的AndFix方案,也就是Hotfix1.X方案。

冷部署

冷部署就是AndFix不支持的代码变更。冷部署针对Davilk和Art分别做了不同的处理:

  • Davilk下,注入追加到PathClassLoader的dexElements,但无需不插桩,通过Hack本地方法从而绕过dvmresolveclass;
  • Art下直接合成一个完整dex,采用手淘目前成熟的art动态部署方案,最后替换PathClassLoader的dexElements即可。

阿里Hotfix2.X SO文件修复方案

  • Davilk和ART下SO文件加载的方式不一样,导致了需要区分Art和Davilk做不同的处理:
  • ART下预Load原来的SO文件,再加载补丁SO文件;
  • Davilk下预Load补丁SO文件,再加载原来的SO文件。

这里的关键是:综合机型支持的Abis和补丁包中的Abis共同决定补丁SO的新LibPath。这两种加载方式都需要对加载两次SO文件,势必会增加一次本地内存的消耗,因此为了达到更好的性能,在Hotfix2.X中提供了下面两个接口替换掉系统加载SO文件的接口:

  • SOPatchManager.load(String libPath) 代替 System.load(String pathName)
  • SOPatchManager.loadLibrary(String libName)代替 System.loadLibrary(String libName)

阿里Hotfix2.X 资源文件修复方案

在资源文件中,资源ID编码于Resources.arsc文件中,排布紧密,并按照排布顺序进行自动编号;RES目录保存所有带ID的资源文件。布局文件为二进制形式的XML文件,XML以资源ID的方式引用其他资源;Assets目录存放所有原始文件,不带ID;Aapt进行资源的构造,包括自动分配资源ID与R文件的生成,默认情况下,每次编译不保证和之前包中的ID一致。

目前市面上普遍采用的三种资源修复方案:

  • 差量合成完整的资源包,运行时完整加载资源;这种方案的缺点是:合成资源占用时间和内存,容易引起卡顿。
  • 修改aapt,对以后可能新增的资源提前留空,运行时Patch包中新增资源ID对应留出的位置;该方案的缺点是:需改变打包流程,修改代码并编译替换SDK中的Aapt;打包侵入太强,且留空占用一定磁盘空间。留空多少是预先定好的,无法改变。
  • 插件化,组件化资源;这种方式的缺点是:资源需要划分模块,提前规划。

百川资源文件修复方案直接基于新旧两个APK来构造补丁包,不需要改造AAPT,对编译过程无要求;同时,精确比较各个资源ID的使用情况,最大程度利用原先基线包资源,补丁包中只包含新增和修改的资源;在运行时无需合成操作,快速应用生效,不影响性能。

百川资源文件修复方案不仅仅是简单修复,对于任意程度、乃至天翻地覆的修改都能适用,但补丁文件会比较大,该方案兼容Android所有机型,只需选取新旧两个APK,一键快捷生成补丁,并且稳定性较好;配合类修复方案,能够做到资源修复的实时生效。在使用百川资源文件修复方案,需要注意以下地方:

(1)如果事先自己做了资源混淆,需要保证新旧包混淆的关系保持一致,否则打补丁时会找不到原来基线包中资源,而将非新增资源视为新增资源,导致补丁包变大。

(2)建议每次打包时设置去除无用的资源。这样即可以减小包大小,同时也保证补丁包中新增资源都是有用的。

(3)AndroidManifest中引用的资源无法改变。有些资源如icon是安装时固定的,目前所有补丁方案都无法进行改变。而另一些资源,如Theme,我们可以提取AndroidManifest中的资源信息,通过代码的方式进行设置。

阿里Hotfix管理后台服务

阿里Hotfix后台目前提供的服务主要有:补丁灰度发布/正式发布、补丁回滚和补丁安全。

  • 补丁灰度发布/正式发布,在发布前可以通过本地或扫码两种方式验证之后再发布上线; 本地补丁模式是指补丁可以放到任何一个指定的目录下即可;扫码模式是扫描二维码生成一个下载URL,然后直接下载,此时不需同服务器验证身份;灰度发布指定具体的用户数然后随机推送。
  • 补丁回滚是指发生错误时可以回滚到目标补丁版本,同时该应用版本下的所有设备都会回滚到目标补丁的版本。
  • 补丁安全方面,HotFix后台托管了RSA密钥,同在在补丁加载时需要进行安全签名校验。

在不远的将来未来,将在阿里HotFix平台上推出以下服务:

  • 补丁自定义平台无关AES秘钥。在打补丁时,用户可以自定义AES密码,然后在SDK初始化时填入这个秘钥即可。阿里百川平台对该秘钥完全无感知,做到补丁在后台的绝对安全。
  • 补丁条件下发,支持分系统版本、分渠道以及自定义TAG的方式下发补丁到目标位置。
  • 实时显示补丁加载成功率等数据,后续可能会上报补丁加载失败详情, 方便排查问题。
  • 一键清除补丁,使用回滚功能必需要具备两个条件:①当前的版本已停止发布;②- 该版本之前存在至少一个历史版本。所以如果第一个补丁就下发错误的话,补丁回滚就无能为力了,所以需要提供一键清除补丁的功能。

在Swift Hot Patch上的进展

在Swift 3推出之后,也是有越来越多的项目转向了Swift。那么,在业界技术分享言必称Swift的时代,Hot Patch对Swift的支持情况又是怎样呢?

image.png

目前业界已有两种成熟的产品:WAX和JSPatch。前者是基于Lua语言的Patch方案;后者是基于JavaScript语言的Patch方案。两者都依托于Objective-C的Runtime,通过Method Swizzling将待Patch方法的实现替换为_objc_msgForward/_objc_msgForward_stret,再替换forwardInvocation:为特定语言的桥接方法来调用补丁实现。

由于两者都强依赖Objective-C的Runtime和NSObject的forward机制,它们在支持Swift Hot Patch时就会受到相应约束。

image.png

相比于WAX和JSPatch,Rollout.io是更面向Swift的Hot Patch方案。它在编译器swiftc前增加了代码注入逻辑,修改swift相关的代码。这样,swiftc在编译时使用的就是经过注入的代码了。这里以viewDidLoad方法为例,说明代码的注入:首先,根据方法ID取出Patch所需的数据;接着,判断是否应该Patch;如果应该Patch,则以Patch数据、target、方法入参和方法调用closure为参数执行Patch。经过注入,Patch就可以替代任何方法的原始实现。

然而,Rollout.io的实现有很多问题:它支持的Patch非常受限,只能patch class instance function;此外,它并不支持在Patch中调用Swift方法,调用Objective-C方法时,它的语法也非常繁琐。

image.png

HotFix的Swift Hot Patch融合了WAX/JSPatch和Rollout.io的优长,并补足了Rollout.io在Patch方面的短板。具体来说,除了在class function patch时没有WAX/JSPatch和Rollout.io的诸多限制外,也同时支持global function、struct function和enum function的Patch。在调用Objective-C方法时,维持了简短的语法;同时,也正在研究动态调用Swift方法的方式。

image.png

SteveMcConnell在《CodeComplete》中曾说过“Program into your language, not in it”。希望Swift Hot Patch工具能够帮助更多的开发者深入Swift,自由地使用语言去表达自己的思想,而不只是停留在语法层面。

相关文章
|
7月前
|
IDE 小程序 开发工具
【社区每周】商家平台数据中心功能升级;IDE 3.4.1 Beta 版本上线(1月第一期)
【社区每周】商家平台数据中心功能升级;IDE 3.4.1 Beta 版本上线(1月第一期)
67 11
|
7月前
|
供应链 安全 IDE
重磅发布|OpenSCA SaaS全新上线
OpenSCA SaaS全新上线!
91 0
|
数据可视化 前端开发 JavaScript
【技术战疫】G2 4.0 正式版发布!
![image.png](https://ata2-img.cn-hangzhou.oss-pub.aliyun-inc.com/b38d8b3099e0f743dffb1e0f535b0f0c.png) > G2 是一套面向常规统计图表,以数据驱动的高交互可视化图形语法。 经历了为期半年的大规模底层重构和开发迭代,G2 4.0 终于和大家见面了。作为继 2017 年 11 月 2
【技术战疫】G2 4.0 正式版发布!
在EMAS应用研发平台上进行热修复补丁的生成可能遇到的问题
在EMAS应用研发平台上进行热修复补丁的生成可能遇到的问题
78 1
|
SQL Java 大数据
ChunJun 1.16 Release版本即将发布,bug 捉虫活动邀您参与!
【ChunJun 1.16 新版本 bug 捉虫活动开始啦!奖品丰厚,等你来拿!】 ChunJun 即将迎来 1.16 Release 版本的正式发布。由于升级初期测试验证的场景覆盖不全,需要更多小伙伴的力量一起来帮忙验证,发现和修复问题,因此 ChunJun 社区决定广发英雄帖,邀请各路捉虫达人,一起帮助 ChunJun 在稳定性上更上一层楼。我们也将送出智能音箱、星巴克礼品卡、蓝牙耳机等多项好礼作为报答。 详情请点进正文查看~
147 0
ChunJun 1.16 Release版本即将发布,bug 捉虫活动邀您参与!
|
开发工具 git
《百度万人研发团队 Git 工具链建设的挑战与思考》电子版地址
百度万人研发团队 Git 工具链建设的挑战与思考
80 0
《百度万人研发团队 Git 工具链建设的挑战与思考》电子版地址
|
Devops
《重磅发布正式版 DevOps 三十六计》电子版地址
重磅发布正式版 DevOps 三十六计
170 0
《重磅发布正式版 DevOps 三十六计》电子版地址
|
数据采集 监控 Dubbo
官宣!Hippo4j 1.4.x 新版本正式发布,探索更多玩法
官宣!Hippo4j 1.4.x 新版本正式发布,探索更多玩法
335 0
|
存储 数据可视化 中间件
|
监控 人机交互 持续交付
低调不了!最佳体验尽在 Erda 2.0 版本
大家期待已久的 Erda V2.0 带着全新界面风格和特性改进震撼发布! 本次版本升级也意味着 Erda 在技术层面不断提升的同时,在用户体验上也投入了大量精力。界面作为人机交互的重要“桥梁”,我们希望用户感受到的不仅仅是 Erda 的强大功能,更希望给大家呈现优雅、舒适的操作体验!那么下面我们一起来看看本次新版本将会有哪些亮点和大家见面~
178 0
低调不了!最佳体验尽在 Erda 2.0 版本