【Android 热修复】热修复原理 ( 加载 Dex 文件到内存中 | DexClassLoader | PathClassLoader | 反射 Element[] dexElements )(三)

简介: 【Android 热修复】热修复原理 ( 加载 Dex 文件到内存中 | DexClassLoader | PathClassLoader | 反射 Element[] dexElements )(三)

二、本博客涉及代码


     

// 修复包可能有多个, 如先后进行了多次修复 , 存在多个修复包 Dex 文件
        // 这些 Dex 文件按照时间顺序进行放置
        // 之前已经将 SD 卡中的 /storage/emulated/0/update.dex 文件拷贝到了
        // 原应用内置存储空间 /data/user/0/kim.hsl.hotfix/app_odex/update.dex
        // /data/user/0/kim.hsl.hotfix/app_odex/ 目录文件
        File filesDir = context.getDir("odex", Context.MODE_PRIVATE);
        // 获取 /data/user/0/kim.hsl.hotfix/app_odex/ 目录下的所有文件
        File[] listFiles = filesDir.listFiles();
        // 缓存 odex 文件的目录 , 将 dex 优化为 odex 文件
        String optimizedDir = filesDir.getAbsolutePath() + File.separator + "cache_odex";
        // 过滤文件, 系统打包都是 classes.dex , classes1.dex , classes2.dex 等文件
        // 上传的更新包 update.dex 以 .dex 为结尾
        // 以上面两个条件作为过滤的依据
        for (File file : listFiles){
            if (file.getAbsolutePath().startsWith("classes") ||
                    file.getAbsolutePath().endsWith(".dex")){
                // 将 dex 文件加载到内存中
                // 该 DexClassLoader 是 BaseDexClassLoader 的子类
                // BaseDexClassLoader 中有 DexPathList pathList 成员
                // 构造该类时 , 会自动将 dex 文件进行优化为 odex , 然后加载到上述 DexPathList pathList 中
                //
                // 参数一 : Dex 文件路径
                // 参数二 : 缓存路径, 指的是缓存 Odex 文件的目录
                // 参数三 : Dex 中的 lib 库路径, 可以设置 null
                // 参数四 : 上下文的 ClassLoader
                DexClassLoader dexClassLoader = new DexClassLoader(
                        file.getAbsolutePath(),
                        optimizedDir,
                        null,
                        context.getClassLoader());
                // 该 PathClassLoader 是用于加载查找 Android 应用所有 dex 文件的类加载器
                // 将上面获取的 dexClassLoader 中的 DexPathList pathList
                // 插入到 PathClassLoader 中的 DexPathList pathList 成员中
                PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
                // BaseDexClassLoader 中的 DexPathList pathList 是 private 私有的
                // 无法直接获取
                // 需要使用反射机制获取该 Dex 数组
                // 拿到 PathClassLoader (继承 BaseDexClassLoader 类) 对象后
                // 先使用反射机制获取 private final DexPathList pathList 成员
                // 然后再次通过反射 , 获取 DexPathList 中的 private final Element[] dexElements 成员
                try {
                    // 加载系统的 Element[] dexElements ---------------------------------------------
                    // 反射获取 BaseDexClassLoader 类对象
                    Class systemBaseDexClassLoaderClass =
                            Class.forName("dalvik.system.BaseDexClassLoader");
                    // 反射获取 BaseDexClassLoader 中的 private final DexPathList pathList 字段
                    Field systemPathListField =
                            systemBaseDexClassLoaderClass.getDeclaredField("pathList");
                    // 由于是私有成员字段 , 需要设置可访问性
                    systemPathListField.setAccessible(true);
                    // 获取系统的 PathClassLoader pathClassLoader 对象的
                    // private final DexPathList pathList 成员
                    Object systemPathListObject = systemPathListField.get(pathClassLoader);
                    // 获取 DexPathList 类
                    Class systemPathListClass = systemPathListObject.getClass();
                    // 获取 DexPathList 类中的 private final Element[] dexElements 成员字段
                    Field systemDexElementsField =
                            systemPathListClass.getDeclaredField("dexElements");
                    // 由于是私有成员字段 , 需要设置可访问性
                    systemDexElementsField.setAccessible(true);
                    // 获取 DexPathList pathList 对象的 Element[] dexElements 成员
                    Object systemDexElementsObject =
                            systemDexElementsField.get(systemPathListObject);
                    // 系统的 Element[] dexElements 加载完毕-----------------------------------------
                    // 上述反射的是系统的 PathClassLoader 的对象
                    // 下面开始反射在本次循环方法中加载的 DexClassLoader dexClassLoader
                    // 加载自己的 Element[] dexElements ---------------------------------------------
                    // 反射获取 BaseDexClassLoader 类对象
                    Class myBaseDexClassLoaderClass =
                            Class.forName("dalvik.system.BaseDexClassLoader");
                    // 反射获取 BaseDexClassLoader 中的 private final DexPathList pathList 字段
                    Field myPathListField =
                            myBaseDexClassLoaderClass.getDeclaredField("pathList");
                    // 由于是私有成员字段 , 需要设置可访问性
                    myPathListField.setAccessible(true);
                    // 获取系统的 PathClassLoader pathClassLoader 对象的
                    // private final DexPathList pathList 成员
                    Object myPathListObject = myPathListField.get(pathClassLoader);
                    // 获取 DexPathList 类
                    Class myPathListClass = myPathListObject.getClass();
                    // 获取 DexPathList 类中的 private final Element[] dexElements 成员字段
                    Field myDexElementsField =
                            myPathListClass.getDeclaredField("dexElements");
                    // 由于是私有成员字段 , 需要设置可访问性
                    myDexElementsField.setAccessible(true);
                    // 获取 DexPathList pathList 对象的 Element[] dexElements 成员
                    Object myDexElementsObject = myDexElementsField.get(myPathListObject);
                    // 自己的 Element[] dexElements 加载完毕-----------------------------------------
                    // 将系统 PathClassLoader pathClassLoader 的
                    // DexPathList pathList 对象的 Element[] dexElements 成员
                    // systemDexElementsObject
                    // 与
                    // 自己在程序中的 DexClassLoader dexClassLoader 的
                    // DexPathList pathList 对象的 Element[] dexElements 成员
                    // myDexElementsObject
                    // 进行融合
                    // 将 myDexElementsObject 插入到 systemDexElementsObject
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }






三、 源码资源


源码资源 :


GitHub 地址 : https://github.com/han1202012/HotFix

CSDN 源码快照 : https://download.csdn.net/download/han1202012/16651312


目录
相关文章
|
Unix 程序员 Linux
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
【OSTEP】动态内存开辟 | 内存API常见错误 | UNIX: brk/sbrk 系统调用 | mmap创建匿名映射区域 | mmap创建以文件为基础的映射区域
266 0
|
2月前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
51 0
|
2月前
|
程序员 Windows
程序员必备文件搜索工具 Everything 带安装包!!! 比windows自带的文件搜索快几百倍!!! 超级好用的文件搜索工具,仅几兆,不占内存,打开即用
文章推荐了程序员必备的文件搜索工具Everything,并提供了安装包下载链接,强调其比Windows自带搜索快且占用内存少。
48 0
|
3月前
|
存储 安全 Linux
将文件映射到内存,像数组一样访问
将文件映射到内存,像数组一样访问
34 0
|
5月前
|
Java
jmap 查看jvm内存大小并进行dump文件内存分析
jmap 查看jvm内存大小并进行dump文件内存分析
105 3
|
7月前
|
存储 缓存 Java
释放C盘空间:释放Windows休眠文件和关闭虚拟内存
在 Windows 11 专业版中,可以通过以下步骤来释放休眠文件(Hibernate File),以释放磁盘空间。休眠文件是系统休眠(Hibernate)功能所需要的文件,它保存了系统的当前状态,以便在休眠状态下恢复。如果你不使用休眠功能,如果因为C盘空间不足,可以考虑释放这个文件来腾出磁盘空间。
16676 1
|
6月前
|
Python
python3获取内存和cpu利用率记录日志文件psutil
python3获取内存和cpu利用率记录日志文件psutil
73 1
|
6月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
50 0
有 3 个进程 P1、P2、P3 协作解决文件打印问题。P1 将文件记录从磁盘读入内存的缓冲区 1,每执行一次读一个记录 ;P2 将缓冲区 1 中的内容复制到缓冲区 2 中,每执行一次复制一个记录 ;
有 3 个进程 P1、P2、P3 协作解决文件打印问题。P1 将文件记录从磁盘读入内存的缓冲区 1,每执行一次读一个记录 ;P2 将缓冲区 1 中的内容复制到缓冲区 2 中,每执行一次复制一个记录 ;
|
7月前
|
Windows
虚拟机内存越用越少,即使文件都永久删除了!!!
虚拟机内存越用越少,即使文件都永久删除了!!!