当前位置: 首页 > 科技观察

微信Android热补丁实践演进之路

时间:2023-03-21 01:51:30 科技观察

继插件之后,热补丁技术在2015年开始爆发,已经是非常火爆的Android开发技术。其中,淘宝的Dexposed、支付宝的AndFix、QQ空间的超级热补丁解决方案等比较有名。微信对热补丁技术的研究并不算早,大约从2015年6月开始。在研究和尝试了各种现有的解决方案后,我们发现它们都存在一些自身的局限性。微信最终采用了与他们不同的技术方案,走上了属于自己的实用进化之路。另一方面,技术应该只是热补丁解决方案的一部分。通过热补丁的多次尝试和应用,微信建立了自己的流程规范,同时也在不断尝试拓展自己的应用场景。通过这篇文章,希望大家不仅可以全面了解各种热补丁技术的优缺点,还可以更全面地了解其应用场景。在此基础上,大家可能更容易决定在自己的项目中是否使用热补丁技术,以及如何使用。1、为什么需要热补丁?热补丁:使应用无需重装即可更新,帮助应用快速建立动态修复能力。从上面的定义来看,热补丁节省了大量Android应用市场的发布时间。同时,用户无需重新安装,只要上线即可无感知更新。看起来不错,但这是否意味着我们可以尝试使用补丁而不是发布?事实上,热补丁技术还是有局限性的,主要有以下几点:补丁只能针对单一的客户端版本,随着版本差异变大,补丁的大小也会增加;补丁不能支持所有的修改,比如AndroidManifest;更新补丁代码或资源的成功率达不到100%。既然补丁技术不能完全替代升级,那么它适用于哪些场景呢?2.轻量级快速升级热补丁技术也可以理解为动态修改代码和资源的通道,适用于修改量较小的场合。以微信多次发布为例,补丁大小都在300K以内,相比传统发布有很大优势。基于安卓用户的升级习惯,即使是相对活跃的微信也需要10多天才能覆盖到50%的用户。使用补丁技术,我们可以在一天内达到70%以上的覆盖率。这也是基于补丁体积小,可以直接使用移动??网络下载更新。因此,补丁技术非常适合在灰度阶段使用。以往,我们需要确保在正式发布之前修复所有严重的问题,这通常需要我们经历三个以上的灰度过程,并且无法在同一批次中快速验证这些问题的修复效果用户。利用热补丁技术,我们可以为同一批用户快速验证修复效果,大大缩短了我们的发布流程。如果发布的版本出现问题或者紧急漏洞,传统的方法需要对修改进行单独的灰度验证,然后重新发布新的版本。利用补丁技术,我们只需要小部分用户上线验证修改的效果,最后全面上线。但是,这种发布对在线用户的影响很大,需要谨慎。本着对用户负责的态度,发布一个补丁就相当于发布一个版本,也应该严格执行完整的测试和上线流程。总的来说,补丁技术可以降低开发成本,缩短开发周期,实现轻量级快速升级。3、远程调试一入Android深似海,Android开发的另一个痛点就是模型的碎片化。我们可能都会遇到“本地不复发”、“找不到日志”、“联系用户不死你”的烦恼。所以补丁机制非常适合远程调试。也就是我们需要有只给特定用户发送补丁的能力,这对我们发现问题很有帮助。使用补丁技术,避免骚扰用户,默默为用户排忧解难。当然,这也需要非常严格的权限管理,以防止恶意或随意使用。4.数据统计数据统计在微信中也占据了非常重要的位置,我们也非常希望能把热补丁和数据统计更好的结合起来。事实上,热补丁在通用统计和ABTest上都有很大的优势。比如我想对同一批用户做两个测试,传统的方法是无法让这些用户安装两个版本的。利用补丁技术,我们可以方便的为同一批用户更换补丁版本。在数据统计的道路上,如何更好地与补丁技术结合,更加精准地控制样本数量和比例,也是微信目前努力发展的一个方向。5.其他其实Android官方也使用了热补丁技术来实现InstantRun。分为热插拔、温插拔和冷插拔三种方式。您可以参考参考文章中的英文介绍或翻译。最新的InstantApp应该也是采用类似的原理,但是GooglePlay不允许代码分发,这个海外App需要注意。六、微信热补丁技术的演进了解了热补丁技术能做什么,适合做什么,我们回到技术本身。由于Dexposed不能支持所有平台,不适合在商业产品中应用。所以这里我们只简单介绍下Andfix、QQ空间、微信的实现方式,以及他们面临的问题。也可以参考资料中各大热补丁方案的分析对比。1、AndFixAndFix采用原生hook方式。本方案直接使用dalvik_replaceMethod来替换类中方法的实现。由于不是整体替换类,类中字段的相对地址是在类加载时确定的,所以AndFix不支持增加或删除字段(只能通过替换init来修改字段的值)和临床)。正因为如此,Andfix能够支持的补丁场景比较有限,只能用于修复特定的问题。结合之前的发布流程,我们希望补丁不被开发者感知,即他不需要知道修改的是补丁版本还是正式发布版本(其实我们也使用git分支机构管理+cherry-pick方法)。另一方面,使用原生替换会面临更复杂的兼容性问题。与其他程序相比,AndFix最大的优势在于即时生效。AndFix的实现方式其实有点类似于InstantRun的热插拔,但由于使用场景的限制,微信在初期已经排除了使用该方案。2.QzoneQzone的解决方案没有开源,但是github上的Nuwa采用了同样的方法。本方案使用classloader的方式来实现更友好的类替换。而这个类似于我们加载Multidex的方式,基本可以保证稳定性和兼容性。具体原理这里就不细说了,可以参考《AndroidApp热补丁动态修复技术介绍》一文。为了解决意外的DEX问题异常,本方案采用插桩的方式,避免问题的发生。其实Android系统的这些检查规则是很有意义的,无论是Dalvik还是Art,都会给QQ空间的解决带来一些问题。达尔维克;dexopt过程中,如果classverify通过,会写入pre-verifyflag,optimize后写入odex文件。这里的optimize主要包括inline和quickinstruction的优化。如果使用instrumentation,所有类都不会被预验证,这会导致在加载类时触发验证和优化操作。这会造成一定的性能损失。微信做了两次测试,分别使用存根和不存根。一是连续加载700个类50行左右,二是统计微信整个启动耗时。每个类的verify+optimize(和类的大小有关)平均耗时不长,这个耗时每个类只有一次。但是由于启动时会加载大量的类,所以这种情况下的影响还是比较大的。艺术;Art采用了一种新的方法,插桩对代码的执行效率没有影响。但是如果patch中的类修改了类变量或者方法,可能会造成内存地址混乱。为了解决这个问题,我们需要把修改了变量、方法和接口的类的父类和所有调用这个类的类都添加到补丁包中。这会导致补丁大小急剧增加。这是因为fast*硬编码了类在dex2oat期间可以确定的每个地址。如果补丁包的地址在运行时发生变化,原类在调用时会出现地址混乱。这里说的细节可能不够详细。其实为了找出这两个问题,微信也花了一定的时间,基本了解了Dalvik和Art的流程。如果您对此感兴趣,我将在后面单独的文章中详细讨论。总的来说,Q空间方案的优势在于透明、开发简单。该方案目前应用成功率也是最高的,但是在补丁包的大小和性能损失上有一定的局限性。特别是,无论我们是否真的打了补丁,都会因为instrumentation而对程序在运行时的性能产生影响。微信对性能要求很高,所以我们没有采用这个方案。3、有没有微信热补丁方案,可以做到透明开发,没有Q空间方案的缺陷?InstantRun的冷插和Buck的exopackage可能会给我们启发,他们的想法全部用新的Dex代替。也就是说,我们完全使用新的Dex,这样就不存在艺术地址混乱的问题,也不需要在Dalvik中插桩。当然,考虑到补丁包的大小,我们不能直接把新的Dex放在里面。但是我们可以把新旧Dex的区别放在补丁包里。最简单的方法是使用BsDiff算法。简单来说,不同的patch.dex是在编译时通过新旧Dex生成的。运行时,将差异的patch.dex与原安装包的旧Dex恢复到新的Dex。这个过程可能会消耗时间和内存,所以我们把它放在一个后台进程中:patch。为了让补丁包尽可能小,微信开发了自己的DexDiff算法,利用Dex格式缩小差异大小。它的粒度是Dex格式的每一项,可以充分利用原始Dex的信息,而BsDiff的粒度是file,AndFix/Qzone的粒度是class。我希望以后可以用单独的文章来描述这个。这里有个伏笔。大体效果如下图所示。在最极端的情况下,由于一个13M的Dex被原dex的信息完全替换了,所以我们的patchsize只有6.6M。但是这种方案也不是没有缺点,它带来了两个问题:占用Rom体积;这里大约是你修改后的Dex大小的1.5倍(Dex压缩成jar的大小加上生成的dexopt文件的大小)。额外的合成过程;虽然我们在单独的进程中处理,但是合成时间的长短和内存消耗也会影响最终的成功率(与修改Dex大小和patch大小有关)。微信的热补丁解决方案叫做Tinker,可以看作是Dota中地精修补匠的回忆,希望能实现永久刷新。限于篇幅,更多的Dex、library和resources的技术细节这里就不详细讨论了,希望以后单独放在一篇文章中。我们将这些方案作为一个整体进行比较:如果不关心性能损失和patchsize,Qzone方案最简单,成功率最高(没有单独的合成过程)。与Tinker相比,它的Rom大小也更小。另一方面,目前QQ空间与Tinker的成功率相差约3%。事实上,一个完整的框架也应该是一个易于使用的框架。Tinker在补丁版本管理、进程管理、安全验证等方面都有很好的支持,同时我们也支持gradle和namedline两种接入方式。希望在不久的将来很快见到你。7、微信热补丁应用现状上一章我们简单对比了各种热补丁的技术方案,解决了补丁包如何生成和加载的问题。但一个完整的热补丁系统不应局限于此,还需要包括以下几个方面:网络通道;这里要解决的问题是决定如何将补丁推送给哪部分用户。在线及后台管理平台;这里主要包括热补丁上线管理、历史管理、报表分析、告警监控等;1.网络通道状态网络通道负责向用户下发补丁包,包括特定用户和完整用户。健康)状况。事实上,微信目前有以下三个热补丁更新通道:拉通道;在登录/24小时的时候,通过pull方式查看后台是否有对应的补丁包更新,这也是我们最常用的方式;指定版本推送通道;对于版本通道,在紧急情况下,我们可以在一小时内向所有用户发布补丁包更新。指定特定用户的推送通道;对特定用户或用户组进行远程调试。其实对于大部分应用来说,假设不实现push通道,实现CDN+pull通道是比较容易的。2、在线及管理平台现状在线及管理平台主要用于快速上线、历史记录管理、补丁运行状态监控等(界面比较丑,因为我们没美工).其实微信发布热补丁是非常用心的。它的整个发布过程和升级后的版本是一致的,版本号也要修改,并且要经过严格完整的测试过程。我们也会灰度上线,同时监控补丁版本的各项指标。这里,为了全面监控补丁状态,我们做了如下操作:每小时/天各版本累计用户1分钟粒度,实时监控补丁版本数量和活跃度;3分钟粒度的Crash统计,benchmark版本和补丁VersionCrash每小时/每天二维对比;10分钟粒度补丁监控信息上报。3.补丁成功率当前应用成功率=补丁版本人数/补丁发布前版本人数由于可能存在基线或者补丁版本的用户已经安装了其他版本,所以这个统计结果应该略低,但能真实反映补丁在线覆盖的有效性。使用QQ空间方案,10天后微信补丁申请成功率约为98.5%。Tinker的使用率只有95.5%左右,主要原因是空间不足,后台进程被杀。这里我们也在尝试使用重试的方式,减少合成的时间和内存消耗,从而提高成功率。热补丁技术发展迅猛,Android推出的InstantApp也令人期待。但在中国,我们似乎还是期望自己更可靠。每个应用程序的要求并不一致。下面分享一些微信的实践经验,希望对大家有所帮助。八、未来工作随着微信部门从“单一APP”向“多APP”演进,微信也正在步入开源开发的实践。我们希望将每个功能组件化,以便快速复制和应用。微信的热补丁框架“Tinker”目前正在经历从微信中分离出来并融入微信的过程。我们希望在不久的将来,我们也可以在微信中开源“Tinker”和其他一些组件。我们也希望能找到一些内测的app,给我们提供宝贵的意见。如果对微信的Tinker解决方案感兴趣,可以单独发消息或在文末留言,注明姓名、公司、负责APP。我们希望选择一些产品进行内部测试。