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開發的朋友能夠加一下,有什麼新技術你們一塊兒交流學習一下,整理了一些乾貨,須要的話,能夠進羣找管理免費領取,很少說直接上圖吧!