AndFix解析——(下)

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

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

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

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);
		}
	}
}

不知道大家是否还记得之前提到的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 fail
		return;
	}

	//这段代码的源码中的注释很清楚,我就不写了
	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.html
		if (mSecurityChecker.verifyOpt(optfile)) {
			saveFingerprint = false;
		} else if (!optfile.delete()) {
			return;
		}
	}
    
    //start
	final DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),
			optfile.getAbsolutePath(), Context.MODE_PRIVATE);

	if (saveFingerprint) {
		mSecurityChecker.saveOptSig(optfile);
	}

	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;
		}
	};
	//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) {
	//对每个类创建一个不会冲突的key
    String key = clz + "@" + classLoader.toString();
    //mFixedClass是一个Map<String, Class<?>>,并被实例化为ConcurrentHashMap<>();
    Class<?> clazz = mFixedClass.get(key);
    if (clazz == null) {// class not load
    	Class<?> clzz = classLoader.loadClass(clz);
    	// initialize target class and modify access flag of classfields to public
    	clazz = AndFix.initTargetClass(clzz);
    }
    if (clazz != null) {// initialize class OK
    	mFixedClass.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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值