美团热更新原理分析

作者: AIl_Blue | 来源:发表于2018-07-06 14:41 被阅读83次

    引言

    安卓技术学习图谱(持续更新中,欢迎关注)
    https://github.com/Sakuragi/android-summary

    可以参考较早版本的美团热更新原理
    https://tech.meituan.com/android_robust.html

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

    • 生成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变量,就可以达到修复的目的

    相关文章

      网友评论

        本文标题:美团热更新原理分析

        本文链接:https://www.haomeiwen.com/subject/qjzvuftx.html