热修复简单版原理实现

2021-04-09  本文已影响0人  马路牙子666

用到生成dex命令
dx --dex --output = xx.dex com\xxxx\xxx\xxx.class

public class HotFixUtil {

    private final static String PATH_LIST_FIELD = "pathList";//pathList 属性
    private final static String DEX_ELEMENTS_FIELD = "dexElements";//dexElements 属性
    /**
     * makeDexElements(List<File> files, File optimizedDirectory,
     * List<IOException> suppressedExceptions, ClassLoader loader)
     */
    private final static String MAKE_DEX_ELEMENTS = "makeDexElements";//将dex 变成 elements 数组

    public static void install(Application application, File dexFile) throws IllegalAccessException, InvocationTargetException {
        //1.获取 当前项目类加载器
        ClassLoader classLoader = application.getClassLoader();
        //2.获取类加载器中的 pathList
        Field pathListField = findField(classLoader, PATH_LIST_FIELD);
        Object pathList = pathListField.get(classLoader);
        //3.获取旧的 dexElements 数组

        Field fieldElement = findField(pathList, DEX_ELEMENTS_FIELD);
        Object[] oldElements = (Object[]) fieldElement.get(pathList);

        //4.根据新的dex 文件获取需要插入的dexElements 数组
        //第一个参数
        List<File> dexFiles = new ArrayList<>();
        dexFiles.add(dexFile);
        //第二个参数
        File optimizedDirectory = application.getCacheDir();
        //第三个参数
        List<IOException> suppressedExceptions = new ArrayList();
        Method method = findMethod(pathList, MAKE_DEX_ELEMENTS, List.class, File.class, List.class, ClassLoader.class);
        Object[] dexElements = (Object[]) method.invoke(pathList, dexFiles, optimizedDirectory, suppressedExceptions, classLoader);

        //5.将两个dexElements 合并成一个新的
        Class<?> componentType = dexElements.getClass().getComponentType();
        Object[] newElements = (Object[]) Array.newInstance(componentType, oldElements.length + dexElements.length);
        System.arraycopy(dexElements, 0, newElements, 0, dexElements.length);
        System.arraycopy(oldElements, 0, newElements, dexElements.length, oldElements.length);
        //6.将新的dexElements 数组设置给 pathList
        fieldElement.set(pathList, newElements);
    }


    /**
     * 根据name 查询对象中属性
     * 如果当前类没有 就去父类找
     *
     * @param instance
     * @param fieldName
     * @return
     */
    private static Field findField(Object instance, String fieldName) {
        Class<?> aClass = instance.getClass();
        while (aClass != null) {
            try {
                Field field = aClass.getDeclaredField(fieldName);
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                return field;
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
            aClass = aClass.getSuperclass();
        }
        return null;
    }

    /**
     * 查找对象中对应方法
     *
     * @param instance
     * @param methodName
     * @return
     */
    private static Method findMethod(Object instance, String methodName, Class<?>... args) {
        Class<?> aClass = instance.getClass();
        while (aClass != null) {
            try {
                Method method = aClass.getDeclaredMethod(methodName, args);
                if (!method.isAccessible()) {
                    method.setAccessible(true);
                }
                return method;
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            aClass = aClass.getSuperclass();
        }
        return null;
    }

}

上一篇下一篇

猜你喜欢

热点阅读