本文是在Android8.1.0上进行的源码分析,首先明确几个classloader的定义。
BootClassLoader:由Framework里面,Android系统自带的类进行加载的时候,用的是BootClassLoader。比如Activity.class.getClassLoader返回的就是BootCloassLoader。
PathClassLoader:由用户自定义或者第三方库中定义的classLoader,比如MainActivity.class.getClassLoader返回的就是PathClassLoader
dexFile相当于一个classes.dex,而每一个dexFile也是一个Element,热修复的补丁包一定要插到classes.dex中的最前面的一个dex开始查找,因为dexFile是从classes.dex classes2.dex依次向后进行查找的.前面如果找到就不会找后面的类了。

热修复过程
反射工具类:
//ShareReflectUtil.java
public static Field findField(Object instance,String name){
Class<?> cls =instance.getClass();
while(cls!=Object.class){
try{
Field field = cls.getDeclareField(name);
if(field !=null){
filed.setAccessible(true);
return field;
}
}catch(NoSuchFieldException){
e.printStackTrace();
}
cls = cls.getSuperclass();
}
}
public static Field findMethod(Object instance,String name,Class<?> ... paramterTypes){
Class<?> cls =instance.getClass();
while(cls!=Object.class){
try{
Method method = cls.getDeclareMethod(name,paramterTypes);
if(method !=null){
method.setAccessible(true);
return method;
}
}catch(NoSuchFieldException){
e.printStackTrace();
}
cls = cls.getSuperclass();
}
}
热修复:
//HotFix.java
public static void installPatch(Application application){
//1.获取到当前应用的PathClassLoader
ClassLoader classLoader = application.getClassLoader();
try{
//2.反射获取到DexPathList属性对象pathList,根据源码BaseClassLoader的pathListo获取
Field pathListField = ShareReflectUtil.findField(classLoader,"pathList");
Object pathList = pathListField.get(classLoader);
//3.反射修改pathList的dexElements
//3.1 把补丁包patch.dex转换成Element[](patch)
List<File> files = new ArrayList<>();
files.add(patchFile);
File dexOutputDir = application.getCodeCacheDir();
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
Method makePathElements = ShareReflectUtil.findMethod(pathList,"makePathElements",List.class,File.class,List.class);
//反射执行
makePathElements.invoke(null,files,dexOutputDir,suppressedExceptions);
//3.2 获得pathList的dexElements属性(old)
Field dexElementsField =ShareReflectUtil.findField(pathList,"dexElements");
Object[] dexElements = (Object[]) dexElementsField.get(pathList);
//3.3 patch +old 合并,并反射赋值给pathList的dexElements
// 创建新的数组来装在 两个数组中所有的元素
//Element[].class Element.class pathElements[0].getClass patchElemets[0].getClass
Object[] dexElements = (Object[])Array.newInstance(pathElements.getClass().getComponentType(),
patchElements.length+dexElements.length);
System.arraycopy(pathElements,0,newElements,0,patchElements.length);
System.arraycopy(dexElements,0,newElements,patchElements.length,dexElements.length);
//最终打包成dex成功替换
dexElements.set(pathList.newElements);
}catch(Exception e){
e.printStackTrace();
}
}
成功使用:
public class MyApplication extends Application {
protected void attachBaseContext(Context base){
super.attachBaseContext(base);
Hotfix.installPatch(this,new File("/sdcard/patch.jar"););
}
}