阿里Sophix热修复

阿里巴巴对Android热修复技术已经进行了长达多年的探索。

最开始,是手淘基于Xposed进行了改进,产生了针对Android Dalvik虚拟机运行时的Java Method Hook技术,Dexposed。但这个方案由于对底层Dalvik结构过于依赖,最终无法继续兼容Android5.0以后ART虚拟机,因此作罢。

后来支付宝提出了新的热修复方案Andfix。Andfix同样是一种底层结构替换的方案,也达到了运行时生效即时修复的效果,并且重要的是,做到了Dalvik和ART环境的全版本兼容。阿里百川结合手淘在实际工程中使用Andfix的经验,对相关业务逻辑解耦后,推出了阿里百川Hotfix方案,并得到了良好的反响。

此时的百川Hotfix已经是一个很不错的产品了,对于基本的代码修复需求都可以解决,安全性和易用性都做的比较好。然而,它所依赖的基石,Andfix本身,是有局限性的。且不说其底层固定结构的替换方案稳定性不好,其使用范围也存在着诸多限制,虽然可以通过改造代码绕过限制来达到相同的修复目的,但这种方式既不优雅也不方便。而更大的问题是,Andfix只提供了代码层面的修复,对于资源和so的修复都还未能实现。

再看一下同期的其他热修复方案,此时的热修复技术可谓是百花齐放,微信的Tinker、QQ空间的Nuwa、饿了么的Amigo、美团的Robust等等,各个热修复方案争相发布,都声称自己可以做到全方位全功能的热修复。不过他们各自有自身的局限性,或者不够稳定,或者补丁过大,或者效率低下,或者使用起来过于繁琐,大部分技术上看起来似乎可行,但实际体验并不好。而在我们看来,有很多技术细节能够做得更加完美。

终于在2017年6月11日,手淘技术团队联合阿里云正式发布了新一代Android移动热修复方案——Sophix

Sophix的横空出世,将会打破各家热修复技术纷争的局面。我们可以满怀信心地说,在Android热修复的三大领域:代码修复、资源修复、so修复方面,以及方案的安全性和易用性方面,Sophix都做到了业界领先。

设计理念

Sophix的核心设计理念,就是非侵入性。

我们的打包过程不会侵入到apk的build流程中。我们所需要的,只有已经生成完毕的新旧apk,而至于apk是如何生成的——是Android Studio打包出来的、还是Eclipse打包出来的、或者是自定义的打包流程,我们一律不关心。在生成补丁的过程中间既不会改变任何打包组件,也不插入任何AOP代码,我们极力做到了——不添加任何超出开发者预期的代码,以避免多余的热修复代码给开发者带来困扰。

在Sophix中,唯一需要的就是初始化和请求补丁两行代码,甚至连入口Application类我们都不做任何修改,这样就给了开发者最大的透明度和自由度。我们甚至重新开发了打包工具,使得补丁工具操作图形界面化,这种所见即所得的补丁生成方式也是阿里热修复独家的。因此,Sophix的接入成本也是目前市面上所有方案里最低的。

这种非侵入式热更新理念,是我们在设计过程中从用户使用角度进行了深入思考而提炼出的核心思想。

这里的用户,指的自然是广大的开发者。对于开发者而言,热修复应该是一个与业务无关的SDK组件,在整个开发过程中感知不到它的存在。最理想的情况,就是开发者拿过来两个apk,一个是已经安装在手机上的apk,另一个是将要发布出去的apk。我们直接通过工具,就可以根据这两个apk生成补丁,然后把这个补丁下发给已经安装的旧app上,就可以直接加载,使旧app重生为新的app。而这个加载了补丁包新app,在功能和使用上,将会和直接安装新apk别无二致。

至于Sophix这个名字,是来源于Sophic(明智的)+ FIX,一个更明智的热修复方案。

详细比较

下面的这张表格,从几个热修复最重要的维度,把Sophix和另外两个主要商业化热修复方案进行了比较。

方案对比 Sophix Tinker Amigo
DEX修复 同时支持即时生效和冷启动修复 冷启动修复 冷启动修复
资源更新 差量包,不用合成 差量包,需要合成 全量包,不用合成
SO库更新 插桩实现,开发透明 替换接口,开发不透明 插桩实现,开发透明
性能损耗 低,仅冷启动情况下有些损耗 高,有合成操作 低,全量替换
四大组件 不能新增 不能新增 能新增
生成补丁 直接选择已经编好的新旧包在本地生成 编译新包时设置基线包 上传完整新包到服务端
补丁大小
接入成本 傻瓜式接入 复杂 一般
Android版本 全部支持 全部支持 全部支持
安全机制 加密传输及签名校验 加密传输及签名校验 加密传输及签名校验
服务端支持 支持服务端控制 支持服务端控制 支持服务端控制

可以看到,Sophix在各个指标上全面占优。而其中唯一支持不完善的地方就是四大组件,四大组件可以修改代码,但是无法做到新增。这是因为如果要新增四大组件,必须在AndroidManifest里面预先插入代理组件,并且尽可能声明所有权限,而这么做就会给原先的app添加很多臃肿的代码,对app运行流程的侵入性很强,所以,本着对开发者透明与代码极简的原则,我们没有做这种多余的处理。

直接看表格的话,其中有些技术细节可能还看不太明朗,那么接下来,我将从各个角度,深度解读Sophix的技术优势以及它与同类技术的差别。

技术分析

Sophix的诞生,起初是对原先的阿里百川Hotfix 1.X版本进行升级衍进。

原先百川Hotfix服务端的整套请求控制流程,以及安全检查这部分,是与热修复功能相对分离的,因此我们依旧保留了这部分的逻辑。

而原本的热修复方案,主要限制在于Andfix本身,我们最开始也是从突破原先修复限制入手,希望能够基于原先的Andfix代码做一些必要的改进。然而最终发现,Andfix自身限制几乎是无法绕过的,在运行时对原有类的结构是已经固化在内存中的,它的一些动态属性和很难进行扩展。并且由于Android系统的碎片化,厂商的虚拟机底层结构都不是确定的,因此直接基于原先机制进行扩展的风险很大。

所以我们绕开了具体的技术实现细节,直接从修复的原理入手,对原先的代码修复技术进行深挖和改良。

回顾为期九个多月的探索与开发,这其中无处不体现着我们对易用性和优雅性的极致追求,在技术先进性与易用性上我们达到了完美的平衡。所以,当我们再回头看目前市面上的其他热修复技术,真的有一种“曾经沧海难为水”的感觉。

代码修复

代码修复有两大主要方案,一种是阿里系的底层替换方案,另一种是腾讯系的类加载方案。

这两类方案各有优劣:

  • 底层替换方案限制颇多,但时效性最好,加载轻快,立即见效。
  • 类加载方案时效性差,需要重新冷启动才能见效,但修复范围广,限制少。

底层替换方案。

底层替换方案是在已经加载了的类中直接替换掉原有方法,是在原来类的基础上进行修改的。因而无法实现对与原有类进行方法和字段的增减,因为这样将破坏原有类的结构。

一旦补丁类中出现了方法的增加和减少,就会导致这个类以及整个Dex的方法数的变化。方法数的变化伴随着方法索引的变化,这样在访问方法时就无法正常地索引到正确的方法了。如果字段发生了增加和减少,和方法变化的情况一样,所有字段的索引都会发生变化。并且更严重的问题是,如果在程序运行中间某个类突然增加了一个字段,那么对于原先已经产生的这个类的实例,它们还是原来的结构,这是无法改变的。而新方法使用到这些老的实例对象时,访问新增字段就会产生不可预期的结果。

这是这类方案的固有限制,而底层替换方案最为人诟病的地方,在于底层替换的不稳定性。

传统的底层替换方式,不论是Dexposed、Andfix或者其他安全界的Hook方案,都是直接依赖修改虚拟机方法实体的具体字段。例如,改Dalvik方法的jni函数指针、改类或方法的访问权限等等。这样就带来一个很严重的问题,由于Android是开源的,各个手机厂商都可以对代码进行改造,而Andfix里ArtMethod的结构是根据公开的Android源码中的结构写死的。如果某个厂商对这个ArtMethod结构体进行了修改,就和原先开源代码里的结构不一致,那么在这个修改过了的设备上,通用性的替换机制就会出问题。这便是不稳定的根源。

而我们也对代码的底层替换原理重新进行了深入思考,从克服其限制和兼容性入手,以一种更加优雅的替换思路,实现了即时生效的代码热修复。我们实现的是一种无视底层具体结构的替换方式,

也就是把原先这样的逐一替换 

变成了这样的整体替换 

这么一来,我们不仅解决了兼容性问题,并且由于忽略了底层ArtMethod结构的差异,对于所有的Android版本都不再需要区分,代码量大大减少。即使以后的Android版本不断修改ArtMethod的成员,只要保证ArtMethod数组仍是以线性结构排列,就能直接适用于将来的Android 8.0、9.0等新版本,无需再针对新的系统版本进行适配了。事实也证明确实如此,当我们拿到Google刚发不久的Android O(8.0)开发者预览版的系统时,hotfix demo直接就能顺利地加载补丁跑起来了,我们并没有做任何适配工作,鲁棒性极好。

具体技术细节,可以看这篇文章:Android热修复升级探索——追寻极致的代码热替换

类加载方案

类加载方案的原理是在app重新启动后让Classloader去加载新的类。因为在app运行到一半的时候,所有需要发生变更的类已经被加载过了,在Android上是无法对一个类进行卸载的。如果不重启,原来的类还在虚拟机中,就无法加载新类。因此,只有在下次重启的时候,在还没走到业务逻辑之前抢先加载补丁中的新类,这样后续访问这个类时,就会Resolve为新类。从而达到热修复的目的。

再来看看腾讯系三大类加载方案的实现原理。QQ空间方案会侵入打包流程,并且为了hack添加一些无用的信息,实现起来很不优雅。而QFix的方案,需要获取底层虚拟机的函数,不够稳定可靠,并且有个比较大的问题是无法新增public函数。

微信的Tinker方案是完整的全量dex加载,并且可谓是将补丁合成做到了极致,然而我们发现,精密的武器并非适用于所有战场。Tinker的合成方案,是从dex的方法和指令维度进行全量合成,整个过程都是自己研发的。虽然可以很大地节省空间,但由于对dex内容的比较粒度过细,实现较为复杂,性能消耗比较严重。实际上,dex的大小占整个apk的比例是比较低的,一个app里面的dex文件大小并不是主要部分,而占空间大的主要还是资源文件。因此,Tinker方案的时空代价转换的性价比不高。

其实,dex比较的最佳粒度,应该是在类的维度。它既不像方法和指令维度那样的细微,也不像bsbiff比较那般的粗糙。在类的维度,可以达到时间和空间平衡的最佳效果。基于这个准则,我们另辟蹊径,实现了一种完全不同的全量dex替换方案。

我们采用的也是全量合成dex的技术,这个技术是从手淘插件化框架Atlas汲取的。我们会直接利用Android原先的类查找和合成机制,快速合成新的全量dex。这么一来,我们既不需要处理合成时方法数超过的情况,对于dex的结构也不用进行破坏性重构。

从图中可以看到,我们重新编排了包中dex的顺序。这样,在虚拟机查找类的时候,会优先找到classes.dex中的类,然后才是classes2.dex、classes3.dex,也可以看做是dex文件级别的类插桩方案。这个方式十分巧妙,它对旧包与补丁包中classes.dex的顺序进行了打破与重组,最终使得系统可以自然地识别到这个顺序,以实现类覆盖的目的。这将会大大减少合成补丁的开销。

双剑合璧

既然底层替换方案和类加载方案各有其优点,把他们联合起来不是最好的选择吗?Sophix的代码修复体系正是同时涵盖了这两种方案。两种方案的结合,可以实现优势互补,完全兼顾的作用,可以灵活地根据实际情况自动切换。

这两种方案我们都进行了重大的改进,并且从补丁生成到应用的各个环节都进行了研究,使得二者能很好地整合在一起。在补丁生成阶段,补丁工具会根据实际代码变动情况进行自动选择,针对小修改,在底层替换方案限制范围内的,就直接采用底层替换修复吗,这样可以做到代码修复即时生效。而对于代码修改超出底层替换限制的,会使用类加载替换,这样虽然及时性没那么好,但总归可以达到热修复的目的。

另外,运行时阶段,Sophix还会再判断所运行的机型是否支持热修复,这样即使补丁支持热修复,但由于机型底层虚拟机构造不支持,还是会走类加载修复,从而达到最好的兼容性。

资源修复

目前市面上的很多资源热修复方案基本上都是参考了Instant Run的实现。实际上,Instant Run的推出正是推动这次热修复浪潮的主因,各家热修复方案,在代码、资源等方面的实现,很大程度上地参考了Instant Run的代码,而资源修复方案正是被拿来用到最多的地方。

简要说来,Instant Run中的资源热修复分为两步:

  1. 构造一个新的AssetManager,并通过反射调用addAssetPath,把这个完整的新资源包加入到AssetManager中。这样就得到了一个含有所有新资源的AssetManager。
  2. 找到所有之前引用到原有AssetManager的地方,通过反射,把引用处替换为AssetManager。

我们发现,其实大量代码都是在处理兼容性问题和找到所有AssetManager的引用处,真正的替换的逻辑其实很简单。

我们的方案没有直接使用Instant Run的技术,而是另辟蹊径,构造了一个package id为0x66的资源包,这个包里只包含改变了的资源项,然后直接在原有AssetManager中addAssetPath这个包就可以了。由于补丁包的package id为0x66,不与目前已经加载的0x7f冲突,因此直接加入到已有的AssetManager中就可以直接使用了。补丁包里面的资源,只包含原有包里面没有而新的包里面有的新增资源,以及原有内容发生了改变的资源。并且,我们采用了更加优雅的替换方式,直接在原有的AssetManager对象上进行析构和重构,这样所有原先对AssetManager对象的引用是没有发生改变的,所以就不需要像Instant Run那样进行繁琐的修改了。

可以说,我们的资源修复方案,优越性超过了Google官方的Instant Run方案。整个资源替换的方案优势在于:

  1. 不修改AssetManager的引用处,替换更快更完全。(对比Instanat Run以及所有copycat的实现)
  2. 不必下发完整包,补丁包中只包含有变动的资源。(对比Instanat Run、Amigo等方式的实现)
  3. 不需要在运行时合成完整包。不占用运行时计算和内存资源。(对比Tinker的实现)

所以,我们不要被所谓的“官方实现”束缚住手脚,其实Instant Run的开发团队和Android framework的开发团队并不是同一个团队,他们对于Android系统机制的理解未必十分深入。只要你认真研读系统代码,实现一个比官方更好的方案绝非难事。所以我想说的是,要想实现技术方案的突破,首先就需要破除所谓“权威”的观念。

资源修复的更多技术细节,可通过这篇文章一探究竟:Android热修复升级探索——资源更新之新思路

so库修复

so库的修复本质上是对native方法的修复和替换。

我们知道JNI编程中,native方法可以通过动态注册和静态注册两种方式进行。动态注册的native方法必须实现JNI_OnLoad方法,同时实现一个JNINativeMethod[]数组,静态注册的native方法必须是Java+类完整路径+方法名的格式。

动态注册的native方法映射通过加载so库过程中调用JNI_OnLoad方法调用完成,静态注册的native方法映射是在该native方法第一次执行的时候才完成映射,当然前提是该so库已经load过。

我们采用的是类似类修复反射注入方式。把补丁so库的路径插入到nativeLibraryDirectories数组的最前面,就能够达到加载so库的时候是补丁so库,而不是原来so库的目录,从而达到修复的目的。

采用这种方案,完全由Sophix在启动期间反射注入patch中的so库。对开发者依然是透明的。不用像某些其他方案需要手动替换系统的System.load来实现替换目的。

未来展望

热修复的必要性

热修复是一个与业务完全无关的模块,开发者如果要自己实现一套可靠的热修复框架,将花费大量时间和精力。虽然市面上已经有很多开源的热修复实现,然而其中的很多坑,往往要踩过才知道,等你把这些坑一一踩过之后,可能大量的用户已经对你失去信心。所以,依靠一个稳定可靠、而且简单实用的商业版本,反而能使各方面的成本降到最低。并且,热修复并不是简单的客户端SDK,它还包含了安全机制和服务端的控制逻辑,这整条链路也不是短时间内可以快速完成的。

还是那句老话,专业是事交给专业的人去做。开发者应该把更多时间精力放到自己的核心业务之中。

Sophix提供了一套更加完美的客户端服务端一体的热更新方案。做到了图形界面一键打包、加密传输、签名校验和服务端控制发布与灰度功能,让你用最少的时间实现最强大可靠的全方位热更新。并且在代码修复、资源修复、SO库修复方面,都做到了业界最佳。

对Android的生态的影响

很多人会把热修复技术跟其他国内厂商的“黑科技”混为一谈。有人说,你们国内开发者就是瞎搞,就不能给我们Android用户一个更加纯净的环境吗?

这里我需要澄清一下。热修复技术不同于其他国内的Android“黑科技”。就比如,国内Android进程保活,是让app持续驻留在后台避免被系统杀死,这既耗费手机电量又占内存,浪费了很多手机资源。再比如,app自行定制的推送服务,无节操地对用户进行信息轰炸。还有更过分的全家桶,一个app同时拉起一票app,并且长期占着内存,使得手机卡顿不堪。总归,这些技术都是为了app厂商的利益而损害手机使用者的实际体验。

而热修复技术是完全不同的,它达到的是一个手机用户和开发者双赢的目的。不仅厂商可以快速迭代更新app,使得功能能最快上线。并且由于热更新过程是毫无感知的,手机用户也减少了繁琐的更新步骤,节省了大量等待更新的时间。这实际上是改善了Android的生态环境。只是这其中最重要的,是要保证热修复功能的稳定性。而Sophix的稳定性,是经过了无数开发者检验的,并且还有手淘多年深厚的技术沉淀作为保障。

Android与iOS热修复的不同

前段时间,苹果封杀了iOS的热修复功能,很多人就因此不看好热修复技术了。这里我想说的是,苹果的政策并不能证明他有多先进,相反,作为独裁者,苹果做过很多不得人心的事,就比如前段时间封杀微信的文章打赏功能。热修复功能被禁止,会使得很多app不得不靠直接发版进行更新,这样一旦新版本出了问题,整个更新迭代过程变得十分漫长。并且一些试验性功能无法进行灰度,这就使得一个重要功能的更新将直接全量发版,如果功能不够稳定,波及范围就变得非常广。而且,用户需要重新下载整个app,不仅流程漫长,原本不到1MB的补丁就能解决的事,现在不得不下载几十MB的完整包才能更新。

再看回Android的情况,Android热修复和iOS是有极大不同的。主要有两个方面:

  1. 谷歌和苹果在中国的地位不同
  2. Android和IOS的开放性不同

谷歌在中国没有像苹果那样的控制力,即使它想要封杀也不可能,国内是有各个安卓应用市场的,没有统一的app安装渠道。另外,Android是开源的,各个厂商都可以做定制,想统一各家的安装渠道几乎是不可能的。

未来,无限可能!

我们对于未来是很乐观的,Android的热修复领域不仅不会受到封杀,反而还有很大的发展空间。我们正在尝试支持各大加固厂商,目前阿里聚安全修复已经支持了Sophix,热修复结合安全加固,将会使得app的稳定性和安全性更加坚不可摧。甚至后续还可以与系统厂商合作,对系统app乃至系统组件进行修复,这样就可以避免频繁OTA升级。

因此,热修复所能发挥是价值将是十分巨大的。热修复还可以与其他领域进行碰撞,引发无限的可能性。在这里,我们欢迎所有应用厂商以及ROM厂商与我们合作,共同使得Android的生态更加完善。

原文地址:https://www.cnblogs.com/linghu-java/p/9794965.html

时间: 2024-10-06 08:37:07

阿里Sophix热修复的相关文章

阿里SopHix热修复框架

2015年以来,Android开发领域里对热修复技术的讨论和分享越来越多,同时也出现了一些不同的解决方案,如QQ空间补丁方案.阿里AndFix以及微信Tinker(Bugly sdk也集成Tikner热更新)和阿里最新出品Sophix.它们在原理各有不同,适用场景各异.不过从技术上来说多数热修复框架是基于ClassLoader加载机制.下面是常见热修复框架的比较. 开通热修复服务 读者可以打开Sophix热修复的官网:Sophix官网1.注册阿里云账号,选择移动热修复服务 ,选择之后需要开通此功

阿里 AndFix 热修复框架简介

阿里AndFix热修复框架简介 热修复原理: Android的类加载机制 Android的类加载器分为两种,PathClassLoader和DexClassLoader,两者都继承自BaseDexClassLoader PathClassLoader代码位于libcore\dalvik\src\main\Java\dalvik\system\PathClassLoader.java DexClassLoader代码位于libcore\dalvik\src\main\java\dalvik\sys

百川解码精彩回顾:热修复的坑和阿里的解

热修复是很多开发者关心的技术,8月27日晚,阿里百川组织了"百川解码"在线直播,以"热修复的坑和阿里的解"为主题,邀请了三位业界嘉宾对热修复技术进行了探讨,并介绍了阿里百川全面接受公测的热修复解决方案:阿里百川HotFix,就网友提出的相关问题进行了解答.本文是此次直播的精彩回顾. 嘉宾简介 歩川,阿里巴巴资深开发工程师,<让App像Web一样发布新版本>一文作者,在OPPO从事Android Framework两年,腾讯QQ空间工作一年半,热衷研究安卓

Android 热修复的相关总结(主要是阿里百川的)

1.主流的热修复是 QQ .微信和阿里百川 2.我建议使用阿里百川的原因第一:团队在钉钉有专门的客服   二.对于新手来说非常方便 3.操作步骤:阿里百川的api文档很详细 我只说一些坑: 1>下面的初始化只能放在oncreat()中 放在其他文件中没什么作用 private void initHotfix() { String appVersion; try { appVersion = this.getPackageManager().getPackageInfo(this.getPacka

什么是热修复?

插件化和热修复技术是Android开发中比较高级的知识点,是中级开发人员通向高级开发中必须掌握的技能,插件化的知识可以查我我之前的介绍:Android插件化.本篇重点讲解热修复,并对当前流行的热修复技术做一个简单的总结. 热修复 什么是热修复? 简单来讲,为了修复线上问题而提出的修补方案,程序修补过程无需重新发版! 技术背景 在正常软件开发流程中,线下开发->上线->发现bug->紧急修复上线.不过对于这种方式代价太大.  而热修复的开发流程显得更加灵活,无需重新发版,实时高效热修复,无

Android之移动热修复

阿里云最近推出了移动热修复服务,听说这个服务傻瓜式接入,性能相对较好,对新技术比较好奇的我决定尝试一下. 移动热修复.png 首先,需要开通这个服务,创建应用 创建应用.png 然后,在项目中接入服务.按照文档所述,第一步:gradle远程仓库依赖, 打开项目找到app的build.gradle文件,添加如下配置:添加maven仓库地址: repositories { maven { url "http://maven.aliyun.com/nexus/content/repositories/

全面了解Android热修复技术

WeTest 导读 本文探讨了Android热修复技术的发展脉络,现状及其未来. 热修复技术概述 热修复技术在近年来飞速发展,尤其是在InstantRun方案推出之后,各种热修复技术竞相涌现.国内大部分成熟的主流APP都拥有自己的热修复技术,像手淘.支付宝.QQ.饿了么.美团等等. 目前能搜集到的资料,大多简单罗列每个方案的特点并进行横向比较,而其中技术发展的脉络往往被掩盖了.热修复技术从何而来,又将往何处去?在这些资料中都找不到答案. 我认为,走马观花地看一遍各家的热修复方案并不能找到答案,所

Android热修复(HotFix)实战

线上的BUG一直是程序员头疼的问题.有时候仅仅是因为几行的代码,就能让你的用户损失严重.谷歌在Android Studio 加入了Insttan Run 机制.通过Apk动态加载的技术实现了应用非安装而进行代码层的改变.之后QQ空间团队的补丁开始了热更新的浪潮.用户不需要通过手动操作,进行App的版本更新,极大的保障了用户对于产品的粘合度,对于开发者而言能使用到这项技术当然再好不过,市面上的热更新,热修复他们的叫称不同,功能相同只是支持的范围有所差别.目前可用的开放平台有腾讯的Tinker以及H

关于Android热修复的几种解决方案

文中引用到的一些博客: http://www.jianshu.com/p/0a31d145cad2 https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731290eef12ad0d17f39d4a 阿里推出方案的同时还出了本热修复的书链接放这里,推荐! http://download.csdn.net/download/xlitol/9892768?spm=5176.d