Android 热修复

本文详细介绍了Android热修复的原理与实现,包括腾讯的Tinker、阿里的AndFix和美团的Robust等主流框架。热修复通过类加载、底层替换等方式实现,避免了因bug导致的APP重新发布。文章探讨了3种修复方式的优缺点,并阐述了热修复的流程,涉及ClassLoader、Dex动态加载等技术。此外,还展示了如何将dex和apk作为插件进行加载的示例代码。
摘要由CSDN通过智能技术生成

1.Android热修复

热修复,就是对线上版本的静默更新。当APP发布上线之后,如果出现了严重的bug,通常需要重新发版来修复,但是重新走发布流程可能时间比较长,重新安装APP用户体验也不友好,所以出现了热修复,热修复就是通过发布一个插件,使APP运行的时候加载插件里面的代码,从而解决缺陷,并且对用户来说是无感的(有时候可能需要重启一下APP)。

热修复的实现方案,一种是类加载方案,即dex插桩,这种思路在插件化中也会用到;还有一种是底层替换方案,即修改替换ArtMethod。采用类加载方案的主要是以腾讯系为主,包括微信的Tinker、qq空间的QZone、美团的Robust、饿了么的Amigo;采用底层替换方案的主要是阿里系的AndFix等。

热修复包括3部分:开发端、服务端和用户端。在开发端,通过Gradle插件生成补丁包,并上传到云端,客户端通过判断是否需要下载新的补丁包,并执行热修复。

d26ebd7fe4fb4a939f695be26a7177da.png

目前主流的热修复框架有腾讯的Tinker、QZone,阿里的AndFix、Sophix,美团的Robust。他们采用的修复方式不同,比如AndFix和Robust采用native层hook Java层代码 bug fix,他们是即时生效的;而Tinker和QZone采用类替换,需要重启APP才能生效。

①阿里的AndFix(已经不再维护)

在native层动态替换java层的方法,通过native层hook java层的代码。通过在native层实现热修复,是不需要重启修复,这是即时生效的。

11bc0185c6e0469aa0ceb8c496f5597c.png

例如方法B中有bug,需要通过热修复替代这个方法。我们知道,所有方法的调用,都会在JVM中入栈,执行完成之后出栈,方法在JVM中是一个ArtMethod结构体,那么在JVM运行这个方法之前,在Native层完成这个方法的替换,那么就完成了热修复的工作,而且是即时生效的。这是基于方法进行修复的。

②美团的Robust

对每个函数都在编译打包阶段自动的插入一段代码,类似于代理,将方法执行的代码重定向到其他方法中。

Robust采用的技术是编译时字节码插桩技术,这个过程在gradle-plugin中发生,在编译打包阶段,对每个函数注入一段逻辑代码,通过判断是否执行插入的这段代码,这个过程也是即时生效的。

③腾讯的Tinker

Tinker采用的是Dex动态加载技术,通过反射的方式,将待修复的类放在dexElements数组的前面,在类加载的时候,首先加载这个待修复的类,因为类加载机制不会重复加载类,达到修复的目的。但这个方式是需要重启生效的(出现bug的类在ClassLoader中是不能替换的,存在缓存中,只能重启重新进行类加载)。

Tinker通过计算对比指定的Base Apk中的dex与修复后的Apk中的dex的区别,生成补丁包,所以补丁包中的内容即为两者差分的描述。运行时将Base Apk中的dex与补丁包进行合成,重启后加载全新的合成后的dex文件。

以上3种方式是目前热修复常见的3种方式,其实各有利弊,像native层处理需要大量的开发成本,跟Robust一样,只能达到修复bug的目的,不能新增类和轻量级的功能;而Tinker则是需要重启才能生效。

 

热修复用到的技术包括ClassLoader类加载机制、Dex动态加载技术 – hook反射、差分打包技术 – bsdiff、字节码插桩 – ASM或Javassist、Gradle插件 – 发布差分包、so库的编译。

 

2.热修复流程

根据类加载机制,可以知道热修复的原理就是将补丁包dex文件放到dexElements数组靠前的位置,这样在加载class时,优先找到补丁包中的dex文件,加载到class之后就不再寻找,从而原来apk里同名的类就不会再使用,达到修复的目的。

知道了原理,实现就很简单了,就是添加新的dex对象到当前app的classLoader对象(也就是BaseDexClassLoader)的pathList里面的dexElements。要添加就要先创建,先使用DexClassLoader加载插件,然后再生成插件的dexElements,最后再添加就好了。

①获取到当前应用的PathClassLoader;

②反射获取到DexPathList属性对象pathList;

③反射修改pathList的dexElement:

(1)把补丁包patch.dex转化为Element[](patch)

(2)获得pathList的dexElements属性(old)

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值