为了账号安全,请及时绑定邮箱和手机立即绑定

美团热更新原理分析

标签:
Android

美团热更新大致原理基于三个步骤:

  • 生成Apk文件时对代码进行插桩(编译期)

  • 生成补丁包(编译期)

  • 获取补丁信息并应用(应用期)

基于美团热更新0.4.72

插桩

例如

public int getCount()
  {    while (this.list == null) {      return i;
    }    return this.list.size();
  }

上面的代码会被插桩成这样:

 public int getCount()
  {    int i = 0;
    ChangeQuickRedirect localChangeQuickRedirect = changeQuickRedirect;
    Class localClass = Integer.TYPE;    if (PatchProxy.isSupport(new Object[0], this, localChangeQuickRedirect, false, 924, new Class[0], localClass))
    {
      localChangeQuickRedirect = changeQuickRedirect;
      localClass = Integer.TYPE;
      i = ((Integer)PatchProxy.accessDispatch(new Object[0], this, localChangeQuickRedirect, false, 924, new Class[0], localClass)).intValue();
    }    while (this.list == null) {      return i;
    }    return this.list.size();
  }

可以看到Robust为每个class增加了个类型为ChangeQuickRedirect的静态成员,而在每个方法前都插入了使用changeQuickRedirect相关的逻辑.通过判断

PatchProxy.isSupport(new Object[0], this, localChangeQuickRedirect, false, 924, new Class[0], localClass)

这行代码是否为真来判断是否执行补丁包的逻辑。当为真的时候可能会执行到accessDispatch从而替换掉之前老的逻辑,达到fix的目的。所以只要初始化类时替换掉ChangeQuickRedirect变量就可以达到修复的目的

生成补丁包

当使用生成补丁包插件时会生成三个文件(XXX为对应修改的类名):

  • PatchesInfoImpl.java

  • XXXPatch.java

  • XXXPatchControl.java

其中:PatchesInfoImpl.java标记着所有修改的类的完整类名以及对应的傀儡补丁类的类名信息,也就是这里的XXXPatchConTrol.java的完整类名。如下

public class PatchesInfoImpl
  implements PatchesInfo{  public List getPatchedClassesInfo()
  {
    ArrayList localArrayList = new ArrayList();
    localArrayList.add(new PatchedClassInfo("com.xxx.xxx.TestAnswerActivity", "com.xxx.xxx.robust.patch.TestAnswerActivityPatchControl"));
    com.meituan.robust.utils.EnhancedRobustUtils.isThrowable = false;    return localArrayList;
  }
}

XXXPatch.java 包含着一个待应用的类中的方法,比如要修改A.jva 中的getInt()方法,XXXPatch就会包含A类中修改后的getInt()的方法

public class APatch{
  A originClass;  
  public APatch(Object paramObject)
  {    this.originClass = ((A)paramObject);
  }  
  public Object[] getRealParameter(Object[] paramArrayOfObject)
  {    if ((paramArrayOfObject == null) || (paramArrayOfObject.length < 1)) {      return paramArrayOfObject;
    }
    Object[] arrayOfObject = new Object[paramArrayOfObject.length];    int i = 0;    if (i < paramArrayOfObject.length)
    {      if (paramArrayOfObject[i] == this) {
        arrayOfObject[i] = this.originClass;
      }      for (;;)
      {
        i += 1;        break;
        arrayOfObject[i] = paramArrayOfObject[i];
      }
    }    return arrayOfObject;
  }  
  public void getInt()
  {    //修改后的代码逻辑
    ...
    
  }
}

XXXPatchControl.java 是一个傀儡类,通过这个类的accessDispatch来调用到被修改的代码逻辑也就是XXXPatch.java 中的对应的类中的方法代码逻辑,如下:

public class APatchControl
  implements ChangeQuickRedirect{  public static final String MATCH_ALL_PARAMETER = "(\\w*\\.)*\\w*";  private static final Map<Object, Object> keyToValueRelation = new WeakHashMap();  
  private static Object fixObj(Object paramObject)
  {
    Object localObject = paramObject;    if ((paramObject instanceof Byte)) {      if (((Byte)paramObject).byteValue() == 0) {        break label32;
      }
    }
    label32:    for (boolean bool = true;; bool = false)
    {
      localObject = new Boolean(bool);      return localObject;
    }
  }  
  public Object accessDispatch(String paramString, Object[] paramArrayOfObject)
  {    try
    {
      APatch localAPatch;      if (paramString.split(":")[2].equals("false")) {        if (keyToValueRelation.get(paramArrayOfObject[(paramArrayOfObject.length - 1)]) == null)
        {
          localAPatch = new APatch(paramArrayOfObject[(paramArrayOfObject.length - 1)]);
          keyToValueRelation.put(paramArrayOfObject[(paramArrayOfObject.length - 1)], null);          break label134;
        }
      }      while ("3134".equals(paramString.split(":")[3]))
      {
        localAPatch.getInt();        return null;
        localAPatch = (APatch)keyToValueRelation.get(paramArrayOfObject[(paramArrayOfObject.length - 1)]);        break label134;
        localAPatch = new localAPatch(null);
      }
    }    catch (Throwable paramString)
    {
      paramString.printStackTrace();      return null;
    }
    label134:    return null;
  }  
  public Object getRealParameter(Object paramObject)
  {
    Object localObject = paramObject;    if ((paramObject instanceof TestAnswerActivity)) {
      localObject = new APatch(paramObject);
    }    return localObject;
  }  
  public boolean isSupport(String paramString, Object[] paramArrayOfObject)
  {    return "3134:".contains(paramString.split(":")[3]);
  }
}

应用时动态加载补丁包并应用

这时我们的apk需要修改线上bug时,拿到补丁包后,会进行动态加载path.dex文件,获取到PatchesInfoImpl.class,然后通过这个对象的getPatchedClassesInfo函数知道需要打补丁的是哪个哪个类,然后再通过反射获取到对应的补丁类,再替换掉需要打补丁的类里的ChangeQuickRedirect变量,就可以达到修复的目的



作者:AIl_Blue
链接:https://www.jianshu.com/p/29b9c44749a2


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消