javassist即时编译技术,美团热修复核心与原理解密01-美团热修复原理

什么是热修复技术?

关于热修复这个名词,并不陌生。相信大家都有过更新window补丁的经历,通过补丁可以动态修复系统的漏洞,只不过这个过程对用户而言是可选及自行操作。

bug ---》

过时

修复技术

那么关于Android平台的热修复技术,简单来说,就是通过下发补丁包,让已安装的客户端动态更新,让用户可以不用重新安装APP,就能够修复软件缺陷的一种技术。

随着热修复技术的发展,不仅可以修复代码,同时可以修复资源文件及SO库。

为什么要使用热修复技术?

在回答这个问题之前,我觉得应该先思考如下几个问题。

  1. 开发上线的版本能保证不存在Bug么?
  2. 修复后的版本能保证用户都及时更新么?
  3. 如何最大化减少线上Bug对业务的影响?
  4.  

从这些角度来说,相信大家应该都能有所体会,热修复技术带来的优势不言而喻。

  1. 可快速修复,避免线上Bug带来的业务损失,把损失降到最低。
  2. 保证客户端的更新率,无须用户进行版本升级安装
  3. 良好的用户体验,无感知修复异常。节省用户下载安装成本。

怎么选择热修复技术方案?

国内主流的技术方案

1、阿里系

名称说明
AndFix 开源,实时生效
HotFix阿里百川,未开源,免费、实时生效
Sophix未开源,商业收费,实时生效/冷启动修复

HotFix是AndFix的优化版本,Sophix是HotFix的优化版本。目前阿里系主推是Sophix。

2、腾讯系

名称说明
Qzone超级补丁QQ空间,未开源,冷启动修复
QFix手Q团队,开源,冷启动修复
Tinker微信团队,开源,冷启动修复。提供分发管理,基础版免费

3、其他

名称说明
Robust 美团, 开源,实时修复
Nuwa大众点评,开源,冷启动修复
Amigo饿了么,开源,冷启动修复

方案对比

方案对比SophixTinkernuwaAndFixRobustAmigo
类替换yesyesyesnonoyes
So替换yesyesnononoyes
资源替换yesyesyesnonoyes
全平台支持yesyesyesnoyesyes
即时生效同时支持nonoyesyesno
性能损耗较少较小较大较小较小较小
补丁包大小较小较大一般一般较大
开发透明yesyesyesnonoyes
复杂度傻瓜式接入复杂较低复杂复杂较低
Rom体积较小Dalvik较大较小较小较小
成功率较高较高一般最高较高
热度
开源noyesyesyesyesyes
收费收费(设有免费阈值)收费(基础版免费,但有限制)免费免费免费免费
监控提供分发控制及监控提供分发控制及监控nononono

参考Tinker及Sophix官方对比

以美团的Robust为例,Robust 的原理可以简单描述为:

1、打基础包时插桩,在每个方法前插入一段类型为 ChangeQuickRedirect 静态变量的逻辑,插入过程对业务开发是完全透明

2、加载补丁时,从补丁包中读取要替换的类及具体替换的方法实现,新建ClassLoader加载补丁dex。当changeQuickRedirect不为null时,可能会执行到accessDispatch从而替换掉之前老的逻辑,达到fix的目的

Robust 官方介绍示例图

下面通过Robust的源码来进行分析。
首先看一下打基础包是插入的代码逻辑,如下:

public static ChangeQuickRedirect redirect;
protected void onCreate(Bundle bundle) {
        //为每个方法自动插入修复逻辑代码,如果ChangeQuickRedirect为空则不执行
        if (redirect != null) {
            if (PatchProxy.isSupport(new Object[]{bundle}, this, redirect, false, 78)) {
                PatchProxy.accessDispatchVoid(new Object[]{bundle}, this, redirect, false, 78);
                return;
            }
        }
        super.onCreate(bundle);
        ...
    }

Robust的核心修复源码如下:

public class PatchExecutor extends Thread {
    @Override
    public void run() {
        ...
        applyPatchList(patches);
        ...
    }
    /**
     * 应用补丁列表
     */
    protected void applyPatchList(List<Patch> patches) {
        ...
        for (Patch p : patches) {
            ...
            currentPatchResult = patch(context, p);
            ...
            }
    }
     /**
     * 核心修复源码
     */
    protected boolean patch(Context context, Patch patch) {
        ...
        //新建ClassLoader
        DexClassLoader classLoader = new DexClassLoader(patch.getTempPath(), context.getCacheDir().getAbsolutePath(),
                null, PatchExecutor.class.getClassLoader());
        patch.delete(patch.getTempPath());
        ...
        try {
            patchsInfoClass = classLoader.loadClass(patch.getPatchesInfoImplClassFullName());
            patchesInfo = (PatchesInfo) patchsInfoClass.newInstance();
            } catch (Throwable t) {
             ...
        }
        ...
        //通过遍历其中的类信息进而反射修改其中 ChangeQuickRedirect 对象的值
        for (PatchedClassInfo patchedClassInfo : patchedClasses) {
            ...
            try {
                oldClass = classLoader.loadClass(patchedClassName.trim());
                Field[] fields = oldClass.getDeclaredFields();
                for (Field field : fields) {
                    if (TextUtils.equals(field.getType().getCanonicalName(), ChangeQuickRedirect.class.getCanonicalName()) && TextUtils.equals(field.getDeclaringClass().getCanonicalName(), oldClass.getCanonicalName())) {
                        changeQuickRedirectField = field;
                        break;
                    }
                }
                ...
                try {
                    patchClass = classLoader.loadClass(patchClassName);
                    Object patchObject = patchClass.newInstance();
                    changeQuickRedirectField.setAccessible(true);
                    changeQuickRedirectField.set(null, patchObject);
                    } catch (Throwable t) {
                    ...
                }
            } catch (Throwable t) {
                 ...
            }
        }
        return true;
    }
}

优点

  • 高兼容性(Robust只是在正常的使用DexClassLoader)、高稳定性,修复成功率高达99.9%
  • 补丁实时生效,不需要重新启动
  • 支持方法级别的修复,包括静态方法
  • 支持增加方法和类
  • 支持ProGuard的混淆、内联、优化等操作

缺点

  • 代码是侵入式的,会在原有的类中加入相关代码
  • so和资源的替换暂时不支持
  • 会增大apk的体积,平均一个函数会比原来增加17.47个字节,10万个函数会增加1.67M



作者:Android进阶架构
链接:https://www.jianshu.com/p/91e943a58273
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值