Android ClassLoader(热修复流程)

本文是在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"););
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容