由Android类加载器源码简析这篇博客可知:可以使用DexClassLoader动态加载含有dex的jar或apk文件,然后就可以使用loadClass()方法来加载Class;而应用(即App)是由PathClassLoader加载的。这两个类都继承了BaseDexClassLoader。它有一个私有的属性pathList,这个属性为DexPathList类型,记录加载的dex和so文件的信息。DexPathList中使用一个Element[]类型的dexElements属性记录加载的dex。如果我们先使用DexClassLoader加载jar或apk文件,然后通过反射获取到它的dexElememts。将其与PathClassLoader中dexElememts合并,然后设置给PathClassLoader的pathList的dexElements属性。这样就可以在App中通过类路径直接获取Class了。这就是动态加载dex的基本原理。
Dex动态加载
反射获取Field的方法代码:
/**
* 获取Class中指定名称的属性Field
* @param clazz Class对象
* @param fieldName 属性名称
* @return 属性Field对象
* @throws NoSuchFieldException 如果未查找到对应属性,则抛出
*/
private Field getField(Class clazz, String fieldName) throws NoSuchFieldException {
Field field = null;
String classStr = clazz.toString();
//从子类向父类循环查找Field
while (clazz != null) {
try {
Log.i("Test", "parent class = " + clazz);
field = clazz.getDeclaredField(fieldName);
if (field != null) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field;
}
} catch (Exception e) {
//忽略错误信息
//e.printStackTrace();
}
//获取父类字节码,从父类查找
clazz = clazz.getSuperclass();
}
//如果未查找到,则抛出异常
if (field == null) {
throw new NoSuchFieldException("field: " + fieldName + " not in " + classStr);
}