Android热修复【实战一】

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

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);
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容