我们接着分析阿里开源的AndFix库, 上一篇分析了Patch类,这个类相当于我们提供补丁的容器,容器里有了东西,我们要对容器进行操作了, 于是开始了我们这次的分析。

在第二篇里,我们添了Patch类的那个坑,那么这篇文章我们就把最后两个坑填一填,即loadPatch()方法和AndFixManager类。

在阿里给的Demo里,我们还有最后的loadPatch()方法没有深入,所以先从loadPatch()方法开始。

PatchManager loadPatch()

public void loadPatch() {mLoaders.put("*", mContext.getClassLoader());// wildcardSet<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);}}
}

不知道大家是否还记得之前提到的mLoaders这个成员变量,隔了这么久,说实话我也忘记了,在这里我先带大家一起回忆一下, private final Map<String, ClassLoader> mLoaders;mLoaders原来是储存不同ClassLoader的Map啊。 好的,我们继续向下进行,在第一篇,通过private的initPatchs()方法,和public的addPatch()方法,将Patch加入了mPatchs这个List, 所以,这里只要去遍历这个List,来获取不同的Patch,并对每个Patch做操作即可。 每个Patch,代表了一个.apatch的文件,getClasses(patchName)代表着这个patchpatchName对应的所有需要修改的类。 patchName从两方面而来,一个是你用apkpatch.jar的时候使用-n选项指定或者默认的,另外一个方面是写入Manifest的时候以 -Classes结尾的key,这个我暂时还没有遇到过,遇到过的同学可以给我讲讲,抱歉博客暂时没有评论功能,可以发邮件给我,airzhaoyn@gmail.com。

好了,这里讲的差不多了,我们继续深入。 可以看到,获取了一个patchName对应的所有的需要修改的类后,就会调用AndFixManager类的fix(File, ClassLoader,List<String>)方法, 先来看看源码

AndFixManager fix(File, ClassLoader,List)

/*** fix* * @param file*            patch file* @param classLoader*            classloader of class that will be fixed* @param classes*            classes will be fixed*/
public synchronized void fix(File file, ClassLoader classLoader,List<String> classes) {//是否是支持的Android版本(在AndFixManager类初始化的时候会修改mSupport变量)if (!mSupport) {return;}//验证这个文件的签名和此应用是否一致if (!mSecurityChecker.verifyApk(file)) {// security check failreturn;}//这段代码的源码中的注释很清楚,我就不写了File optfile = new File(mOptDir, file.getName());boolean saveFingerprint = true;if (optfile.exists()) {// need to verify fingerprint when the optimize file exist,// prevent someone attack on jailbreak device with// Vulnerability-Parasyte.// btw:exaggerated android Vulnerability-Parasyte// http://secauo.com/Exaggerated-Android-Vulnerability-Parasyte.htmlif (mSecurityChecker.verifyOpt(optfile)) {saveFingerprint = false;} else if (!optfile.delete()) {return;}}//startfinal DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),optfile.getAbsolutePath(), Context.MODE_PRIVATE);if (saveFingerprint) {mSecurityChecker.saveOptSig(optfile);}ClassLoader patchClassLoader = new ClassLoader(classLoader) {@Overrideprotected 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;}};//Enumerate the names of the classes in this DEX file.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);}}
}

对这部分的源码解析,从源码中标注//start开始。 我们先看一下DexFile是什么,官方文档这样说:Manipulates DEX files. It is used primarily by class loaders. 就是主要被类加载器使用的操作Dex文件的类。 好了,我们可以继续看源码了,先获取一个DexFile对象的,然后通过匿名内部类实现了一个ClassLoaders的子类,遍历这个Dex文件中所有的类, 如果需要修改的类集合(即从PatchManager loadPatch()方法中传过来的类集合)中在这个Dex文件中找到了一样的类,则使用loadClass(String, ClassLoader)加载这个类, 然后调用fixClass(String, ClassLoader)修复这个类。

AndFixManager fixClass(Class<?>, ClassLoader)

private void fixClass(Class<?> clazz, ClassLoader classLoader) {//使用反射获取这个类中所有的方法Method[] methods = clazz.getDeclaredMethods();//MethodReplace是这个库自定义的Annotation,标记哪个方法需要被替换MethodReplace methodReplace;String clz;String meth;for (Method method : methods) {//找到被MethodReplace注解的方法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);}}
}

在源码中基本把这个方法给解读完了,接下来就要看看它调用的replaceMethod(ClassLoader, String,String, Method)方法

AndFixManager replaceMethod(ClassLoader, String,String, Method)

private void replaceMethod(ClassLoader classLoader, String clz,String meth, Method method) {//对每个类创建一个不会冲突的keyString key = clz + "@" + classLoader.toString();//mFixedClass是一个Map<String, Class<?>>,并被实例化为ConcurrentHashMap<>();Class<?> clazz = mFixedClass.get(key);if (clazz == null) {// class not loadClass<?> clzz = classLoader.loadClass(clz);// initialize target class and modify access flag of class’ fields to publicclazz = AndFix.initTargetClass(clzz);}if (clazz != null) {// initialize class OKmFixedClass.put(key, clazz);//获取名为meth的方法Method src = clazz.getDeclaredMethod(meth,method.getParameterTypes());AndFix.addReplaceMethod(src, method);}
}

这里源代码中的英文注释(作者注释)已经很清楚了,去看看调用的那个静态方法AndFix.addReplaceMethod(src, method);

AndFix addReplaceMethod(Method, Method)

public static void addReplaceMethod(Method src, Method dest) {replaceMethod(src, dest);initFields(dest.getDeclaringClass());
}

replaceMethod是一个native方法,声明如下: private static native void replaceMethod(Method dest, Method src); 由于暂时我对JNI不是很熟悉,所以这里就不分析了。 看看另外一个方法initFields(Class<?>)

/*** modify access flag of class’ fields to public* * @param clazz*            class*/
private static void initFields(Class<?> clazz) {Field[] srcFields = clazz.getDeclaredFields();for (Field srcField : srcFields) {Log.d(TAG, "modify " + clazz.getName() + "." + srcField.getName()+ " flag:");setFieldFlag(srcField);}
}

这里英文注释也很清楚了,只是其中调用了setFieldFlag(srcField);这个我们没见过的方法, 声明为private static native void setFieldFlag(Field field); 又是个JNI方法,暂时不进行分析。

到这里,对阿里AndFix库Java层面上的代码的分析就结束了,如果还想了解native层的代码,我将会放在下一篇,敬请等待。

原文地址: http://yunair.github.io/blog/2015/10/23/AndFix-%E8%A7%A3%E6%9E%90(%E4%B8%8B).html

AndFix解析——(下)相关推荐

  1. AndFix解析——(中)

    我们接着分析阿里开源的AndFix库,上次留下了三个坑,一个方法,两个类,不知道你们是否想急切了解呢? loadPatch()方法和AndFixManager和Patch类. 分析loadPatch( ...

  2. AndFix解析——(上)

    阿里巴巴前一段时间开源了他们用来解决线上紧急bug的一款Android库--AndFix 对Android开发者来说真是一个很好的消息. 基于自己的经验,太长的文字很少有人可以一口气看下来的,所以我打 ...

  3. Laravel5.2之Filesystem源码解析(下)

    2019独角兽企业重金招聘Python工程师标准>>> 说明:本文主要学习下\League\Flysystem这个Filesystem Abstract Layer,学习下这个pac ...

  4. Android之EventBus框架源码解析下(源码解析)

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 EventBus是典型的发布订阅模式,多个订阅者可以订阅某个事件,发布者通过 ...

  5. go float64 转int_深挖Go函数之深度解析(下):可变参数

    接连两篇函数专题深度解析,相信大家已经对函数的语法有了深入的了解. 函数简单使用和基本知识解析 匿名函数和闭包 这次给大家带来了一个函数的特性[可变参数],作为函数专题的结束. 有没有发现? 我们有时 ...

  6. Laravel 学习笔记之 Query Builder 源码解析(下)

    说明:本文主要学习下Query Builder编译Fluent Api为SQL的细节和执行SQL的过程.实际上,上一篇聊到了\Illuminate\Database\Query\Builder这个非常 ...

  7. sprintf函数_三分钟学 Go 语言——函数深度解析(下) 可变参数

    接连两篇函数专题深度解析,相信大家已经对函数的语法有了深入的了解. 函数简单使用和基本知识解析 匿名函数和闭包 五一放假期间,我懂得,估计你们都不想学习. 小熊这两天因为个人种种令人难受的原因,没有能 ...

  8. 分区表理论解析(下):SQL Server 2k52k8系列(二)

    接分区表理论解析(上)   分区方案 对表和索引进行分区的第二步是创建分区方案.分区方案定义了一个特定的分区函数将使用的物理存储结构(其实就是文件组),或者说是分区方案将分区函数生成的分区映射到我们定 ...

  9. spring 源码深度解析_spring源码解析之SpringIOC源码解析(下)

    前言:本篇文章接SpringIOC源码解析(上),上一篇文章介绍了使用XML的方式启动Spring,介绍了refresh 方法中的一些方法基本作用,但是并没有展开具体分析.今天就和大家一起撸一下ref ...

最新文章

  1. Clever Answers in Codewar(Javascript 持续更新)
  2. Sublime Text 3 添加当前时间的制作方法
  3. NOIP2013D1T3货车运输(最大生成树+倍增lca)
  4. front mysql 导出表结构_肿么将mysql的表结构导出到sqlserver中
  5. 测试一下你对IP地址的掌握水平
  6. 我发现Facebook Messenger漏洞可使安卓用户互相监听,获奖6万美元
  7. 2、深入理解 Laravel Eloquent(二)——中间操作流(Builder)
  8. 动态电压与频率调节在降低功耗中的作用
  9. 字符打印流(PrintWriter)
  10. 现代大学英语精读第二版(第六册)学习笔记(原文及全文翻译)——3 - What Is News?(新闻是什么?)
  11. (二)什么是IT售前?为什么需要IT售前?
  12. 2023王道数据结构P40题二.1,关于是否会断链的问题
  13. 写给父亲的语音计算器(语音的加载播放C#,四)
  14. LED 控制技术将日间行车灯的优势发挥到极致
  15. matlab行向量,列向量
  16. 实验:网络常见的9个命令
  17. 初出茅庐的小李第63博客之FastLED库的使用
  18. 贾立平是中科学院计算机所博士,贾立平是哪个大学的博士曝光 贾立平秀恩爱不忘考博士...
  19. 在UE4中完美导入MMD的动作,表情;基本导入镜头,材质---最详细教程
  20. php srs api,srs 身份认证

热门文章

  1. leetcode刷题之树(2)
  2. 科大星云诗社动态20220106
  3. [云炬python3玩转机器学习] 5-3简单线性回归的实现
  4. <马哲>劳动价值论的理论及实践意义
  5. 云炬Qtpy5开发与实战笔记 0搭建开发环境(傻瓜式安装)
  6. 我的个人网站更新了!
  7. MNIST机器学习入门(学习记录)——1
  8. C++类内存分布——深度理解继承与虚函数
  9. 各向异性扩散滤波_原理与算法
  10. [OS复习]进程互斥与同步1