【Android 热修复】热修复原理 ( 合并两个 Element[] dexElements | 自定义 Application 加载 Dex 设置 | 源码资源 )
文章目录
- 一、合并两个 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 设置 | 源码资源 )相关推荐
- 【Android 安全】DEX 加密 ( Application 替换 | 兼容 ContentProvider 操作 | 源码资源 )
文章目录 一. 命中 ActivityThread 中 installProvider 方法的分支三 1. 原理分析 2. 代码实现 二. 在 ContextImpl 的 createPackageC ...
- 【Android 热修复】热修复原理 ( 多 Dex 打包机制 | 多 Dex 支持 | Dex 分包设置 | 开发和产品风格设置 | 源码资源 )
文章目录 一.Dex 打包设置 1.多 Dex 支持 2.Dex 分包设置 3.开发和产品风格设置 ( 非必须 ) 二.完整 build.gradle 配置 1.build.gradle 配置 2.d ...
- 【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )
文章目录 一. Service 组件 onStartCommand 方法分析 1. onStartCommand 函数返回值分析 2. onStartCommand 函数 START_STICKY_C ...
- Android之图片加载框架Picasso源码解析
转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/76645535 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...
- Android开发必会技术!Flutter中网络图片加载和缓存源码分析,完整PDF
起因 事情是这样的. 4年前毕业那会,呆在公司的短视频项目,做 视频.那会做得比抖音还早,但是由于短视频太烧钱了,项目被公司关掉了.当时需要开发横竖屏直播/异步视频的场景,就研究下了市场上的 app, ...
- Android 图片加载框架Gilde源码解析
1.使用Gilde显示一张图片 Glide.with(this).load("https://cn.bing.com/sa/simg/hpb/xxx.jpg").into(imag ...
- 【Android 安全】Android 应用 APK 加固总结 ( 加固原理 | 应用加固完整的实现方案 | 源码资源 )
文章目录 一. APK 加固原理 1. Android 应用反编译 2. ProGuard 混淆 3. 多 dex 加载原理 4. 代理 Application 开发 5.Java 工具开发 6.Ap ...
- 【Android 热修复】热修复原理 ( Dex 文件拷贝后续操作 | 外部存储空间权限申请 | 执行效果验证 | 源码资源 )
文章目录 一.Dex 文件准备 二.外部存储空间权限申请 1.清单文件申请权限 2.动态申请权限 三.文件拷贝 1.文件拷贝 2.执行效果 四. 源码资源 一.Dex 文件准备 在 [Android ...
- 【Android 热修复】热修复原理 ( 修复包 Dex 文件准备 | Dex 优化为 Odex | Dex 文件拷贝 | 源码资源 )
文章目录 一.修复包 Dex 文件准备 二.Odex 优化 三.Dex 文件拷贝 四. 源码资源 一.修复包 Dex 文件准备 异常代码 : 故意写一个异常代码 , 并执行该代码 , 肯定会崩溃 ; ...
最新文章
- php字符串常用算法--字符串加密解密
- Android性能优化(3)
- WeMos下实现小车避障与手机控制
- 使用ASP.NET Core 3.x 构建 RESTful API - 2. 什么是RESTful API
- 顶级分布式开源项目,配上这款可视化工具,真香!
- linux上ftp和lftp冲突,Linux FTP客户端 Lftp 使用方法,该如何解决
- 25条提高iOS App性能的建议和技巧
- 【转】做一名开源社区的扫地僧 (上)
- Python常用模块库下载及安装
- 客户信息管理系统(java)
- Python 情人节超强技能 导出微信聊天记录生成词云,深入讲解Python
- Flipped 阅读笔记
- html语言单词背不下来,单词背不下来
- vue实现ZKT(中控)身份证读卡器读卡功能
- panic和recover的使用规则
- 移动端高清、多屏幕适配方案
- 多边形区域填充算法--扫描线种子填充算法
- 制作android动态壁纸,如何使用LibGDx制作Android动态壁纸?
- SuperMap iClient 9D for MapboxGL地图风格浅析
- 无法打开安装程序包....确认这是一个有效的windows installer程序包