美文网首页
Android热修复【实战一】

Android热修复【实战一】

作者: 西瓜家族 | 来源:发表于2020-03-26 00:04 被阅读0次

    说了那么多的原理,也该手写撸撸代码去实现一下。

    1、生成Dex文件

    将class文件打包成dex文件

    1.1 dx指令程序

    要将class文件打包成dex文件,就需要用到dx指令,这个dx指令类似于java指令。我们知道,java的指令有javac、jar等等,之所以可以使用这类指令,是因为我们有安装过jdk,jdk为我们提供了java指令,相同的,dx指令也需要有程序来提供,它就在Android SDK的build-tools目录下各个Android版本目录之中。


    image.png

    1.2 dx指令的使用

    dx指令的使用跟java指令的使用条件一样,有2种选择:

    配置环境变量(添加到classpath),然后命令行窗口(终端)可以在任意位置使用。
    不配环境变量,直接在build-tools/安卓版本 目录下使用命令行窗口(终端)使用。
    第一种方式参考java环境变量配置即可,这里我选用第二种方式。下面我们需要用到的命令是:

    dx --dex --output=dex文件完整路径 (空格) 要打包的完整class文件所在目录,如:

    dx --dex --output=C:\Users\Administrator\Desktop\dex\classes2.dex C:\Users\Administrator\Desktop\dex

    2、下载Dex文件

    这个需要跟后台老大哥去交流

    3、加载Dex

    3.1 获取到当前应用的PathClassloader;

    3.2 反射获取到DexPathList属性对象pathList;

    3.3 反射修改pathList的dexElements

    把补丁包patch.dex转化为Element[] (patch)
    获得pathList的dexElements属性(old)
    patch+old合并,并反射赋值给pathList的dexElements

    public class Hotfix {
    
        /**
         * 1、获取到当前应用的PathClassloader;
         * <p>
         * 2、反射获取到DexPathList属性对象pathList;
         * <p>
         * 3、反射修改pathList的dexElements
         * 3.1、把补丁包patch.dex转化为Element[]  (patch)
         * 3.2、获得pathList的dexElements属性(old)
         * 3.3、patch+old合并,并反射赋值给pathList的dexElements
         */
        public static void installPatch(Application application, File patch) {
            if (!patch.exists()) {
                return;
            }
            // 获取到当前应用的PathClassloader;
            ClassLoader classLoader = application.getClassLoader();
    
            try {
                /**
                 * 反射获取到PathClassLoader父类BaseDexClassLoader中
                 *  private final DexPathList pathList;
                 *  属性对象pathList;
                 */
                // 反射获得BaseDexClassLoader中的pathList成员变量
                Field pathListFiled = SharedReflectUtils.findFiled(classLoader, "pathList");
                // 设为可访问
                pathListFiled.setAccessible(true);
                // 获得PathClassLoader中的pathList对象
                Object pathList = pathListFiled.get(classLoader);
    
    
                /**
                 * 执行makeDexElements方法,解析我们的补丁包获得dexElements数组
                 * 把补丁包patch.dex转化为Element[]  (patch)
                 */
                // 反射获得pathList中的makeDexElements方法
                Method makePathElements = SharedReflectUtils.findMethod(pathList,
                        "makePathElements",
                        List.class, File.class, List.class);
                // 设为可访问
                makePathElements.setAccessible(true);
                List<Object> patchs = new ArrayList<>();
                patchs.add(patch);
    
                ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
                // 执行makeDexElements方法,解析我们的补丁包获得dexElements数组
                Object[] patchElements = (Object[]) makePathElements.invoke(null, patchs, application.getCacheDir(), suppressedExceptions);
    
                /**
                 * 获得pathList的dexElements属性(old)
                 */
                Field dexElementsFiled = SharedReflectUtils.findFiled(pathList, "dexElements");
                Object[] dexElements = (Object[]) dexElementsFiled.get(pathList);
    
                /**
                 * 3.3、patch+old合并,并反射赋值给pathList的dexElements
                 */
                // 创建新的Element数组
                Object[] newElements = (Object[]) Array.newInstance(patchElements.getClass().getComponentType(),
                        patchElements.length + dexElements.length);
                System.arraycopy(patchElements, 0, newElements, 0, patchElements.length);
                System.arraycopy(dexElements, 0, newElements, patchElements.length, dexElements.length);
    
                dexElementsFiled.set(pathList,newElements);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    public class SharedReflectUtils {
    
        public static Field findFiled(Object object, String name) throws NoSuchFieldException {
            Class<?> cls = object.getClass();  //PathClassLoader.class
            while (cls != Object.class) {
                try {
                    Field field = cls.getDeclaredField(name);
                    if (field != null) {
                        // 设置访问权限
                        field.setAccessible(true);
                        return field;
                    }
                } catch (NoSuchFieldException e) {
    
                }
                cls = cls.getSuperclass();//BaseDexClassLoader.class
            }
            throw new NoSuchFieldException(object.getClass().getSimpleName() + " not find " + name);
        }
    
        public static Method findMethod(Object object, String name, Class<?>... parameterTypes) throws NoSuchMethodException {
            Class<?> cls = object.getClass();
            while (cls != Object.class) {
                try {
                    Method method = cls.getDeclaredMethod(name, parameterTypes);
                    if (method != null) {
                        // 设置访问权限
                        method.setAccessible(true);
                        return method;
                    }
                } catch (NoSuchMethodException e) {
                }
                cls = cls.getSuperclass();
            }
            throw new NoSuchMethodException(object.getClass().getSimpleName() + " not find " + name);
        }
    }
    

    相关文章

      网友评论

          本文标题:Android热修复【实战一】

          本文链接:https://www.haomeiwen.com/subject/dknushtx.html