源码路径: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和虚拟机类型有关。