文章目录

  • 一、合并两个 Element[] dexElements
  • 二、 完整修复包加载工具类
  • 三、 源码资源

一、合并两个 Element[] dexElements


在 【Android 热修复】热修复原理 ( 加载 Dex 文件到内存中 | DexClassLoader | PathClassLoader | 反射 Element[] dexElements ) 博客中已经将 系统加载的 Dex 文件对应的 Element[] dexElements 通过 PathClassLoader 类加载器获取到了 , 同时修复包对应 Dex 文件 Element[] dexElements 通过 DexClassLoader 类加载器获取到了 ;

下面开始将修复包对应的 Element[] dexElements 合并到系统 PathClassLoader 中的 Element[] dexElements 数组中 ;

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

将系统 PathClassLoader pathClassLoader 的 DexPathList pathList 对象的 Element[] dexElements 成员systemDexElementsObject

自己在程序中的 DexClassLoader dexClassLoader 的 DexPathList pathList 对象的 Element[] dexElements 成员myDexElementsObject

进行融合 , 将 myDexElementsObject 插入到 systemDexElementsObject ;

首先要获取 Dex 数组 , 但是 Element 类型无法引用 , 不是公开的 ;

先获取 Element 类型 , 调用对象的 .getClass().getComponentType() 获取 ;

// 获取 Dex 数组 , Element 类型无法引用 , 不是公开的
// 首先获取 Element 类型
// systemDexElementsObject
Class<?> elementClass = systemDexElementsObject.getClass().getComponentType();

获取两个 Element[] dexElements 数组的成员个数 ;

// 获取两个 Element[] dexElements 数组的成员个数
// 系统中的 PathClassLoader 中的 Element[] dexElements 数组大小
int systemDexCount = Array.getLength(systemDexElementsObject);
// 本应用中的 DexClassLoader 中的 Element[] dexElements 数组大小
int myDexCount = Array.getLength(myDexElementsObject);

使用 Array.newInstance 重新创建一个数组, 数组的长度是两个数组之和 ;

// 重新创建一个数组
// 类型 : Class<?> elementClass
// 长度 : systemDexCount + myDexCount
Object elementArray =Array.newInstance(elementClass, systemDexCount + myDexCount);

填充创建的数组 , 这里特别注意 , 数组中的元素的顺序很重要 , 一定要先放置修复包中的数组元素 , 然后放置应用自带的 Dex 数组内容 , 这个顺序一定不能乱 ;

// 填充数组内容, 这里特别注意 , 数组中的元素的顺序很重要 ,
// 同样类型的类 , 在多个 Dex 都存在 , 如果在前面的 Dex 中查找到了 , 就不再向后查找了
// 修复包的 Dex 要放在最前面 , 这样才能起到修复作用// 先放置修复包 Dex
for(int i = 0; i < myDexCount; i ++){// 获取 myDexElementsObject 数组中的第 i 个元素// 放置到 elementArray 数组中的第 i 个元素位置Array.set(elementArray, i,Array.get(myDexElementsObject, i));
}// 再放置系统 Dex
for(int i = 0; i < systemDexCount; i ++){// 获取 systemDexElementsObject 数组中的第 i 个元素// 放置到 elementArray 数组中的第 i + myDexCount 个元素位置Array.set(elementArray,i + myDexCount,Array.get(systemDexElementsObject, i));
}

最后 , 通过反射方法 , 将合并后的 elementArray 数组放置到 PathClassLoader 中的 Element[] dexElements 中 ;

// 通过反射方法
// 将合并后的 elementArray 数组放置到
// PathClassLoader 中的 Element[] dexElements 中
systemDexElementsField.set(systemPathListObject, elementArray);

注意 : 此时热修复还不能生效 , 需要进一步进行分包操作才可以 ;

二、 完整修复包加载工具类


package kim.hsl.hotfix;import android.content.Context;
import android.util.Log;import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;public class FixDexUtils {/*** 加载 Dex 文件* @param context*/public static void loadDex(Context context){// 修复包可能有多个, 如先后进行了多次修复 , 存在多个修复包 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// 参数四 : 上下文的 ClassLoaderDexClassLoader 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(dexClassLoader);// 获取 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// 获取 Dex 数组 , Element 类型无法引用 , 不是公开的// 首先获取 Element 类型// systemDexElementsObjectClass<?> elementClass = systemDexElementsObject.getClass().getComponentType();// 获取两个 Element[] dexElements 数组的成员个数// 系统中的 PathClassLoader 中的 Element[] dexElements 数组大小int systemDexCount = Array.getLength(systemDexElementsObject);// 本应用中的 DexClassLoader 中的 Element[] dexElements 数组大小int myDexCount = Array.getLength(myDexElementsObject);Log.i("TAG", "systemDexCount = " + systemDexCount + " , myDexCount = " + myDexCount);// 重新创建一个数组// 类型 : Class<?> elementClass// 长度 : systemDexCount + myDexCountObject elementArray =Array.newInstance(elementClass, systemDexCount + myDexCount);// 填充数组内容, 这里特别注意 , 数组中的元素的顺序很重要 ,// 同样类型的类 , 在多个 Dex 都存在 , 如果在前面的 Dex 中查找到了 , 就不再向后查找了// 修复包的 Dex 要放在最前面 , 这样才能起到修复作用// 先放置修复包 Dexfor(int i = 0; i < myDexCount; i ++){// 获取 myDexElementsObject 数组中的第 i 个元素// 放置到 elementArray 数组中的第 i 个元素位置Array.set(elementArray, i,Array.get(myDexElementsObject, i));}// 再放置系统 Dexfor(int i = 0; i < systemDexCount; i ++){// 获取 systemDexElementsObject 数组中的第 i 个元素// 放置到 elementArray 数组中的第 i + myDexCount 个元素位置Array.set(elementArray,i + myDexCount,Array.get(systemDexElementsObject, i));}// 通过反射方法// 将合并后的 elementArray 数组放置到// PathClassLoader 中的 Element[] dexElements 中systemDexElementsField.set(systemPathListObject, elementArray);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}}
}

三、 源码资源


源码资源 :

  • GitHub 地址 : https://github.com/han1202012/HotFix
  • CSDN 源码快照 : 到下一篇博客下载 , 该快照目前还跑不起来 ;

注意 : 此时热修复还不能生效 , 需要进一步进行分包操作才可以 ;

【Android 热修复】热修复原理 ( 合并两个 Element[] dexElements | 自定义 Application 加载 Dex 设置 | 源码资源 )相关推荐

  1. 【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 )

    文章目录 一. 命中 ActivityThread 中 installProvider 方法的分支三 1. 原理分析 2. 代码实现 二. 在 ContextImpl 的 createPackageC ...

  2. 【Android 热修复】热修复原理 ( 多 Dex 打包机制 | 多 Dex 支持 | Dex 分包设置 | 开发和产品风格设置 | 源码资源 )

    文章目录 一.Dex 打包设置 1.多 Dex 支持 2.Dex 分包设置 3.开发和产品风格设置 ( 非必须 ) 二.完整 build.gradle 配置 1.build.gradle 配置 2.d ...

  3. 【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )

    文章目录 一. Service 组件 onStartCommand 方法分析 1. onStartCommand 函数返回值分析 2. onStartCommand 函数 START_STICKY_C ...

  4. Android之图片加载框架Picasso源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/76645535 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  5. Android开发必会技术!Flutter中网络图片加载和缓存源码分析,完整PDF

    起因 事情是这样的. 4年前毕业那会,呆在公司的短视频项目,做 视频.那会做得比抖音还早,但是由于短视频太烧钱了,项目被公司关掉了.当时需要开发横竖屏直播/异步视频的场景,就研究下了市场上的 app, ...

  6. Android 图片加载框架Gilde源码解析

    1.使用Gilde显示一张图片 Glide.with(this).load("https://cn.bing.com/sa/simg/hpb/xxx.jpg").into(imag ...

  7. 【Android 安全】Android 应用 APK 加固总结 ( 加固原理 | 应用加固完整的实现方案 | 源码资源 )

    文章目录 一. APK 加固原理 1. Android 应用反编译 2. ProGuard 混淆 3. 多 dex 加载原理 4. 代理 Application 开发 5.Java 工具开发 6.Ap ...

  8. 【Android 热修复】热修复原理 ( Dex 文件拷贝后续操作 | 外部存储空间权限申请 | 执行效果验证 | 源码资源 )

    文章目录 一.Dex 文件准备 二.外部存储空间权限申请 1.清单文件申请权限 2.动态申请权限 三.文件拷贝 1.文件拷贝 2.执行效果 四. 源码资源 一.Dex 文件准备 在 [Android ...

  9. 【Android 热修复】热修复原理 ( 修复包 Dex 文件准备 | Dex 优化为 Odex | Dex 文件拷贝 | 源码资源 )

    文章目录 一.修复包 Dex 文件准备 二.Odex 优化 三.Dex 文件拷贝 四. 源码资源 一.修复包 Dex 文件准备 异常代码 : 故意写一个异常代码 , 并执行该代码 , 肯定会崩溃 ; ...

最新文章

  1. php字符串常用算法--字符串加密解密
  2. Android性能优化(3)
  3. WeMos下实现小车避障与手机控制
  4. 使用ASP.NET Core 3.x 构建 RESTful API - 2. 什么是RESTful API
  5. 顶级分布式开源项目,配上这款可视化工具,真香!
  6. linux上ftp和lftp冲突,Linux FTP客户端 Lftp 使用方法,该如何解决
  7. 25条提高iOS App性能的建议和技巧
  8. 【转】做一名开源社区的扫地僧 (上)
  9. Python常用模块库下载及安装
  10. 客户信息管理系统(java)
  11. Python 情人节超强技能 导出微信聊天记录生成词云,深入讲解Python
  12. Flipped 阅读笔记
  13. html语言单词背不下来,单词背不下来
  14. vue实现ZKT(中控)身份证读卡器读卡功能
  15. panic和recover的使用规则
  16. 移动端高清、多屏幕适配方案
  17. 多边形区域填充算法--扫描线种子填充算法
  18. 制作android动态壁纸,如何使用LibGDx制作Android动态壁纸?
  19. SuperMap iClient 9D for MapboxGL地图风格浅析
  20. 无法打开安装程序包....确认这是一个有效的windows installer程序包

热门文章

  1. linux系统/var目录的作用
  2. leetcode 73 矩阵置零 Python
  3. Docker在Linux上运行NetCore系列(一)配置运行DotNetCore控制台
  4. 构建之法读书笔记03
  5. 乱码问题引申 python 中string和unicode
  6. JavaEE 的基本实现
  7. 基于吉日嘎拉的通用权限管理WebForm版扩展:字典选项管理和缓存管理
  8. DT大数据梦工厂 第51讲
  9. XHTML 结构化:使用 XHTML 重构网站
  10. 分享20个非常有用的Web开发工具和框架