patchManager=newPatchManager(context);git
patchManager.init(appversion);//current versiongithub
patchManager作了如下工做:app
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">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>(); }</pre>
new了个AndFixmanager,看一下ide
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">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; } } }</pre>
在这个类里面主要是检测设备是不是支持的AndFix的设备,YunOS不支持。学习
在SecurityChecker里面获取数字证书和检测应用是否debugablethis
另外是初始化patch路径spa
PachManager.init()里面获取AndFix的版本信息,若是信息一致debug
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">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(); } }</pre>
则initPatchs3d
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">private void initPatchs() { File[] files = mPatchDir.listFiles(); for (File file : files) { addPatch(file); } }</pre>
把路径下的patch加到mPatch列表里面。code
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">patchManager.loadPatch();</pre> <pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">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); } } }</pre>
最终调用fix方法
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(), classes);</pre>
fix方法首先签名验证,通常是文件的MD5,经过以后获取dex文件,
而后实现本身的加载器(只须要继承ClassLoader,并覆盖findClass方法)。
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">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); }</pre>
在类加载器里面,若是知道须要修改的方法(annotation标记的),则调用fixClass去修复bug.
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">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); } } }</pre>
在fixClass里面调用replaceMethod方法用patch里面的方法替换掉要修改的方法。
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">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); } }</pre>
最终调用native层的方法
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">AndFix.*addReplaceMethod*(src, method);</pre>
<pre style="margin: 0px; padding: 0px; font-size: 0.85em; font-family: monospace, serif; overflow: auto; line-height: 1.45; background-color: rgb(248, 248, 248); border-radius: 0.5em; position: relative; border: 1px solid rgb(238, 238, 238);">public static void addReplaceMethod(Method src, Method dest) { try { *replaceMethod*(src, dest); *initFields*(dest.getDeclaringClass()); } catch (Throwable e) { Log.e(*TAG*, "addReplaceMethod", e); } }</pre>
Native里面的replaceMethod和虚拟机类型有关。
源码路径:https://github.com/alibaba/An...
最后介绍个QQ群:979045005,Android开发的朋友能够加一下,有什么新技术你们一块儿交流学习一下,整理了一些干货,须要的话,能够进群找管理免费领取,很少说直接上图吧!