四、加载补丁
4.1 思路
通过上一篇博文,我们知道dex 保存在这个位置
```
BaseDexClassLoader–>pathList–>dexElements
```
apk 的classes.dex 可以从应用本身的DexClassLoader 中获取。
path_dex 的dex 需要new 一个DexClassLoader 加载后再获取。
分别通过反射取出dex 文件,重新合并成一个数组,然后赋值给盈通本身的ClassLoader 的dexElements
4.2 代码实现
加载外部dex,我们可以在Application 中操作。
首先新建一个HotPatchApplication,然后在清单文件中配置,顺便加上读取sdcard 的权限,因为补丁就保存在那里。
HotPatchApplication 代码如下:
```
package com.aitsuki.hotpatchdemo;
import android.app.Application;import android.os.Environment;import andr
oid.util.Log;import java.io.File;import java.lang.reflect.Array;import j
ava.lang.reflect.Field;import dalvik.system.DexClassLoader;
/**
* Created by hp on 2016/4/6.
*/
public class HotPatchApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 获取补丁,如果存在就执行注入操作
String dexPath = Environment.getExternalStorageDirectory().getAb
solutePath().concat("/patch_dex.jar");
File file = new File(dexPath);
if (file.exists()) {
inject(dexPath);
} else {
Log.e("BugFixApplication", dexPath + "不存在");
}
}
/**
* 要注入的dex 的路径
*
* @param path
*/
private void inject(String path) {
try {
// 获取classes 的dexElements
Class<?> cl = Class.forName("dalvik.system.BaseDexClassLoader
");
Object pathList = getField(cl, "pathList", getClassLoader());
Object baseElements = getField(pathList.getClass(), "dexEleme
nts", pathList);
// 获取patch_dex 的dexElements(需要先加载dex)
String dexopt = getDir("dexopt", 0).getAbsolutePath();
DexClassLoader dexClassLoader = new DexClassLoader(path, dexo
pt, dexopt, getClassLoader());
Object obj = getField(cl, "pathList", dexClassLoader);
Object dexElements = getField(obj.getClass(), "dexElements",
obj);
// 合并两个Elements
Object combineElements = combineArray(dexElements, baseElemen
ts);
// 将合并后的Element 数组重新赋值给app 的classLoader
setField(pathList.getClass(), "dexElements", pathList, combin
eElements);
//======== 以下是测试是否成功注入=================
Object object = getField(pathList.getClass(), "dexElements",
pathList);
int length = Array.getLength(object);
Log.e("BugFixApplication", "length = " + length);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
/**
* 通过反射获取对象的属性值
*/
private Object getField(Class<?> cl, String fieldName, Object object)
throws NoSuchFieldException, IllegalAccessException {
Field field = cl.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(object);
}
/**
* 通过反射设置对象的属性值
*/
private void setField(Class<?> cl, String fieldName, Object object, O
bject value) throws NoSuchFieldException, IllegalAccessException {
Field field = cl.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
}
/**
* 通过反射合并两个数组
*/
private Object combineArray(Object firstArr, Object secondArr) {
int firstLength = Array.getLength(firstArr);
int secondLength = Array.getLength(secondArr);
int length = firstLength + secondLength;
Class<?> componentType = firstArr.getClass().getComponentType();
Object newArr = Array.newInstance(componentType, length);
for (int i = 0; i < length; i++) {
if (i < firstLength) {
Array.set(newArr, i, Array.get(firstArr, i));
} else {
Array.set(newArr, i, Array.get(secondArr, i - firstLengt
h));
}
}
return newArr;
}
}
```
- 第一章 热修复设计
- 第一节、AOT/JIT & dexopt 与dex2oat
- 一、AOT/JIT
- 二、dexopt 与dex2oat
- 第二节、热修复设计之CLASS_ISPREVERIFIED 问题
- 一、前言
- 二、建立测试Demo
- 三、制作补丁
- 四、加载补丁
- 五、CLASS_ISPREVERIFIED
- 第三节、热修复设计之热修复原理
- 一、Android 热修复
- 二、Android 虚拟机和编译加载顺序
- 三、混合模式的理解
- 四、源码类到机器执行的文件过程
- 五、补丁包
- 六、类补丁生效原理
- 七、Davlik 虚拟机的限制
- 八、Davlik Class resolved by unexpected DEX: 限制和处理方式
- 九、类加载器的双亲委派加载机制
- 第四节、Tinker 的集成与使用(自动补丁包生成)
- 一、简述
- 二、Tinker 组件依赖
- 三、Tinker 的配置及任务
- 四、Tinker 封装与拓展
- 五、编写Application 的代理类
- 六、常用API
- 七、测试
- 八、细节
- 第二章 插件化设计
- 第一节、Class 文件与Dex 文件的结构解读
- 一、Class 文件
- 二、Dex 文件
- 三、Class 文件和Dex 文件对比
- 第二节、Android 资源加载机制详解
- 第三节、四大组件调用原理
- 第四节、so 文件加载机制
- 第五节、Android 系统服务实现原理
- 第三章 组件化框架设计
- 第一节、阿里巴巴开源路由框——ARouter 原理分析
- 第二节、APT 编译时期自动生成代码&动态类加载
- 第三节、Java SPI 机制
- 第四节、AOP&IOC
- 第五节、手写组件化架构
- 第四章 图片加载框架
- 第一节 图片加载框架选型
- 第二节 Glide 原理分析
- 第三节 手写图片加载框架实战
- 第五章 网络访问框架设计
- 第一节 网络通信必备基础
- 第二节 OkHttp 源码解读
- 第三节 Retrofit2 源码解析
- 第六章 RXJava响应式编程框架设计
- 第一节 RXJava之链式调用
- 第二节 RXJava之扩展的观察者模式
- 第三节 RXJava之事件变换设计
- 第四节 Scheduler 线程控制
- 第七章 IOC架构设计
- 第一节 依赖注入与控制反转
- 第二节 ButterKnife 原理上篇、中篇、下篇
- 第三节 IOC架构设计之Dagger2架构设计
- 第八章 Android架构组件 JetPack
- 第一节 LiveData的工作原理
- 第二节 Navigation 如何解决tabLayout 问题
- 第三节 ViewModel 如何感知View 生命周期及内核原理
- 第四节 Room 架构方式方法
- 第五节 dataBinding 为什么能够支持MVVM
- 第六节 WorkManager 内核揭秘
- 第七节 Lifecycles 生命周期