Android代码热修复详解
java:类加载原理:
当类加载器收到加载类或资源的请求时,通常都是先委托给父类加载器加载,也就是说只有当父类加载器找不到指定类或资源时,自身才会执行实际的类加载过程,具体的加载过程如下:
创新互联是一家集网站建设,江北企业网站建设,江北品牌网站建设,网站定制,江北网站建设报价,网络营销,网络优化,江北网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
1、源 ClassLoader 先判断该 Class 是否已加载,如果已加载,则直接返回 Class,如果没有则委托给父类加载器。
2、父类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则委托给祖父类加载器。
3、依此类推,直到始祖类加载器(引用类加载器)。
4、始祖类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的子类加载器。
5、始祖类加载器的子类加载器尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的孙类加载器。
6、依此类推,直到源 ClassLoader。
7、源 ClassLoader 尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,源 ClassLoader 不会再委托其子类加载器,而是抛出异常
Android类加载:
Android中的类加载器是BootClassLoader、PathClassLoader、DexClassLoader,其中BootClassLoader是虚拟机加载系统类需要用到的,PathClassLoader是App加载自身dex文件中的类用到的,DexClassLoader可以加载直接或间接包含dex文件的文件。
PathClassLoader和DexClassLoader都继承自BaseDexClassLoader,它的一个DexPathList类型的成员变量pathList很重要。DexPathList中有一个Element类型的数组dexElements,这个数组中存放了包含dex文件(对应的是DexFile)的元素。BaseDexClassLoader加载一个类,最后调用的是DexFile的方法进行加载的
下面是代码热修复的两种方式:
1、根据类加载为父委托加载原理 将加载修复后的dex的DexClassLoader插入到PathClassLoader和BootstrapClassLoader中间,也就是将DexClassLoader设置为PathClassLoader的父加载器,将BootstrapClassLoader设置为DexClassLoader 顺序为:BootstrapClassLoader--->DexClassLoader--->PathClassLoader
//根据类加载为父委托加载原理 替换有bug的类放在dexPath中让DexClassLoader优先加载
//类加载顺序:BootstrapClassLoader---->DexClassLoader----->PathClassLoader
public void loadPatchDex(Context context, String dexPath, String optimizedDirectory, String librarySearchPath) {
ClassLoader currentClassLoader = context.getClassLoader();//context加载器(PathClassLoader)
ClassLoader parentClassLoader = currentClassLoader.getParent();//context的父加载器(BootstrapClassLoader)
//加载dexPath加载器
DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath, parentClassLoader);
this.setField(ClassLoader.class, this.mParentFieldName, currentClassLoader, dexClassLoader);//设置当前类加载器的父加载器为dexClassLoader
}
/**
* @param clazz
* @param fieldName 属性名称
* @param target 要设置属性的对象
* @param value 设置属性的值
* @return 设置成功
*/
private boolean setField(Class clazz, String fieldName, Object target, Object value) {
try {
Field field = clazz.getDeclaredField(fieldName);
if (field != null) {
field.setAccessible(true);
field.set(target, value);
}
return true;
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return false;//设置失败
}
2、根据安卓的PathClassLoader加载机制将DexClassLoader加载的新的Dex添加的DexPathList的dexElements属性列表中,将更新的dex加载放在dexElements列表索引之前
public void loadPatchDex2(Context context, String dexPath, String optimizedDirectory, String librarySearchPath) {
ClassLoader pathClassLoader = context.getClassLoader();
//加载dexPath加载器
DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath, pathClassLoader.getParent());
//获取BaseDexClassLoader的DexPathList属性 私有的需要通过反射获取
Object dexPathList1 = this.getFieldValue(BaseDexClassLoader.class, this.mDexPathListFieldName, pathClassLoader);//pathClassLoader的PathList属性
Object dexPathList2 = this.getFieldValue(BaseDexClassLoader.class, this.mDexPathListFieldName, dexClassLoader);
if (dexPathList1 != null && dexPathList2 != null) {
//获取对应pathList中的dexElements属性
Object dexElements1 = this.getFieldValue(dexPathList1.getClass(), this.mElementFieldName, dexPathList1);//当前PathClassLoader中的Element[]属性
Object dexElements2 = this.getFieldValue(dexPathList2.getClass(), this.mElementFieldName, dexPathList2);//DexClassLoader中的Element[]属性
if (dexElements1 != null && dexElements2 != null) {
//将两个Element[]属性合并
Object finalElements = combineArray(dexElements2, dexElements1);
//将合并的值设置给当前的pathClassLoader的Element[]中
this.setField(dexPathList1.getClass(), this.mElementFieldName, dexPathList1, finalElements);
Log.e("HotFixEngine", "loadPatchDex2: success");
}
}
}
/**
* 获取对应属性值
* @param clazz
* @param fieldName 属性名称
* @param target 要操作的对象
* @return
*/
private Object getFieldValue(Class clazz, String fieldName, Object target) {
Field field = this.getField(clazz, fieldName);
if (field != null) {
try {
field.setAccessible(true);
return field.get(target);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
private Field getField(Class clazz, String fieldName) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return null;
}
/**
* 两个数组合并
*
* @param arrayLhs
* @param arrayRhs
* @return
*/
private static Object combineArray(Object arrayLhs, Object arrayRhs) {
Class> localClass = arrayLhs.getClass().getComponentType();
int i = Array.getLength(arrayLhs);
int j = i + Array.getLength(arrayRhs);
Object result = Array.newInstance(localClass, j);
for (int k = 0; k < j; ++k) {
if (k < i) {
Array.set(result, k, Array.get(arrayLhs, k));
} else {
Array.set(result, k, Array.get(arrayRhs, k - i));
}
}
return result;
}
Demo地址:https://github.com/xuguohongai/android/tree/master/AndroidHotFixDemo
名称栏目:Android代码热修复详解
新闻来源:http://ybzwz.com/article/gpiipp.html