Android热更新方案之阿里AndFix 原理以及源码解析

源码路径:https://github.com/alibaba/AndFix

原理:



修复过程:



源码解析过程如下:

patchManager=newPatchManager(context);

patchManager.init(appversion);//current version

 

patchManager做了以下工作:

public PatchManager(Context context) {
   mContext = context;
   mAndFixManager = new AndFixManager(mContext);
   mPatchDir = new File(mContext.getFilesDir(), DIR);
   mPatchs = new ConcurrentSkipListSet<Patch>();
   mLoaders = new ConcurrentHashMap<String, ClassLoader>();
}

new了个AndFixmanager,看一下

public AndFixManager(Context context) {
   mContext = context;
   mSupport = Compat.isSupport();
   if (mSupport) {
      mSecurityChecker = new SecurityChecker(mContext);
      mOptDir = new File(mContext.getFilesDir(), DIR);
      if (!mOptDir.exists() && !mOptDir.mkdirs()) {// make directory fail
         mSupport = false;
         Log.e(TAG, "opt dir create error.");
      } else if (!mOptDir.isDirectory()) {// not directory
         mOptDir.delete();
         mSupport = false;
      }
   }
}

在这个类里面主要是检测设备是否是支持的AndFix的设备,YunOS不支持。

在SecurityChecker里面获取数字证书和检测应用是否debugable

另外是初始化patch路径

PachManager.init()里面获取AndFix的版本信息,如果信息一致

public void init(String appVersion) {
   if (!mPatchDir.exists() && !mPatchDir.mkdirs()) {// make directory fail
      Log.e(TAG, "patch dir create error.");
      return;
   } else if (!mPatchDir.isDirectory()) {// not directory
      mPatchDir.delete();
      return;
   }
   SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
         Context.MODE_PRIVATE);
   String ver = sp.getString(SP_VERSION, null);
   if (ver == null || !ver.equalsIgnoreCase(appVersion)) {
      cleanPatch();
      sp.edit().putString(SP_VERSION, appVersion).commit();
   } else {
      initPatchs();
   }
}

则initPatchs

private void initPatchs() {
   File[] files = mPatchDir.listFiles();
   for (File file : files) {
      addPatch(file);
   }
}

把路径下的patch加到mPatch列表里面。

patchManager.loadPatch();
public void loadPatch() {
   mLoaders.put("*", mContext.getClassLoader());// wildcard
   Set<String> patchNames;
   List<String> classes;
   for (Patch patch : mPatchs) {
      patchNames = patch.getPatchNames();
      for (String patchName : patchNames) {
         classes = patch.getClasses(patchName);
         mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(),
               classes);
      }
   }
}

最终调用fix方法

mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(),
      classes);

fix方法首先签名验证,一般是文件的MD5,通过之后获取dex文件,

然后实现自己的加载器(只需要继承ClassLoader,并覆盖findClass方法)。

ClassLoader patchClassLoader = new ClassLoader(classLoader) {
      @Override
      protected Class<?> findClass(String className)
            throws ClassNotFoundException {
         Class<?> clazz = dexFile.loadClass(className, this);
         if (clazz == null
               && className.startsWith("com.alipay.euler.andfix")) {
            return Class.forName(className);// annotation’s class
                                    // not found
         }
         if (clazz == null) {
            throw new ClassNotFoundException(className);
         }
         return clazz;
      }
   };
   Enumeration<String> entrys = dexFile.entries();
   Class<?> clazz = null;
   while (entrys.hasMoreElements()) {
      String entry = entrys.nextElement();
      if (classes != null && !classes.contains(entry)) {
         continue;// skip, not need fix
      }
      clazz = dexFile.loadClass(entry, patchClassLoader);
      if (clazz != null) {
         fixClass(clazz, classLoader);
      }
   }
} catch (IOException e) {
   Log.e(TAG, "pacth", e);
}

在类加载器里面,如果知道需要修改的方法(annotation标记的),则调用fixClass去修复bug.

private void fixClass(Class<?> clazz, ClassLoader classLoader) {
   Method[] methods = clazz.getDeclaredMethods();
   MethodReplace methodReplace;
   String clz;
   String meth;
   for (Method method : methods) {
      methodReplace = method.getAnnotation(MethodReplace.class);
      if (methodReplace == null)
         continue;
      clz = methodReplace.clazz();
      meth = methodReplace.method();
      if (!isEmpty(clz) && !isEmpty(meth)) {
         replaceMethod(classLoader, clz, meth, method);
      }
   }
}

在fixClass里面调用replaceMethod方法用patch里面的方法替换掉要修改的方法。

private void replaceMethod(ClassLoader classLoader, String clz,
      String meth, Method method) {
   try {
      String key = clz + "@" + classLoader.toString();
      Class<?> clazz = mFixedClass.get(key);
      if (clazz == null) {// class not load
         Class<?> clzz = classLoader.loadClass(clz);
         // initialize target class
         clazz = AndFix.initTargetClass(clzz);
      }
      if (clazz != null) {// initialize class OK
         mFixedClass.put(key, clazz);
         Method src = clazz.getDeclaredMethod(meth,
               method.getParameterTypes());
         AndFix.addReplaceMethod(src, method);
      }
   } catch (Exception e) {
      Log.e(TAG, "replaceMethod", e);
   }
}

最终调用native层的方法

AndFix.addReplaceMethod(src, method);

 

public static void addReplaceMethod(Method src, Method dest) {
   try {
      replaceMethod(src, dest);
      initFields(dest.getDeclaringClass());
   } catch (Throwable e) {
      Log.e(TAG, "addReplaceMethod", e);
   }
}

 

Native里面的replaceMethod和虚拟机类型有关。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值