类的加载机制
需要注意的地方
- 1.每次生成之后一定要测试;
- 2.尽量的不要分包,不要分多个dex
- 3.混淆的时候,设计到NDK AndFix.java 不要混淆
- 4.生成包之后一般会加固什么的,这个时候生成的差分包,一定要在之前去生成。
- 5.既然是去修复方法,第一个不能增加成员变量,不能增加方法
源码
public class FixDexManager {
private Context mContext;
private File mDexFile;
private static final String TAG = FixDexManager.class.getSimpleName();
private Object applicationDexElements;
public FixDexManager(Context context) {
this.mContext = context;
//可以访问的dex目录
mDexFile = context.getDir("odex", Context.MODE_PRIVATE);
}
/**
* 修复dex包
*/
public void fixDex(String fixDexPath) throws Exception {
//2.获取下载好的补丁 dexElement
//2.1移动到系统能够访问的 dex目录下
File srcFile = new File(fixDexPath);
if (!srcFile.exists()) {
throw new FileNotFoundException(fixDexPath);
}
File destFile = new File(mDexFile, srcFile.getName());
if (destFile.exists()) {
Log.d(TAG, "patch [" + fixDexPath + "] has be loaded.");
return;
}
copyFile(srcFile, destFile);
//2.2ClassLoader读取fileDex路径
List<File> fixDexFiles = new ArrayList<>();
fixDexFiles.add(destFile);
}
/**
* 把dexElement注入到classLoader
*/
private void injectDexElement(ClassLoader classLoader, Object dexElements) throws Exception {
//1.先获取pathList
Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
//所有属性都可以获得
pathListField.setAccessible(true);
Object pathList = pathListField.get(classLoader);
//2.获得pathList中的dexElements
Field dexElementsFiled = pathList.getClass().getDeclaredField("dexElements");
dexElementsFiled.setAccessible(true);
//注入
dexElementsFiled.set(dexElementsFiled.get(pathList), dexElements);
}
/**
* 合并数组
*/
private static Object combineArray(Object arrayLhs, Object arrayRhs) {
Class<?> localClass = arrayLhs.getClass().getComponentType();//获得数组对象
int i = Array.getLength(arrayLhs);
int j = Array.getLength(arrayRhs);
Object result = Array.newInstance(localClass, j);
for (int k = 0; k < j; ++k) {
if (k < i) {
Array.set(result, k, Array.get(arrayLhs, k));
} else {
Array.set(result, k, Array.get(arrayLhs, k - 1));
}
}
return result;
}
/**
* 将路径复制到目标文件下
*/
public static void copyFile(File src, File dest) throws IOException {
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
if (!dest.exists()) {
dest.createNewFile();
}
inChannel = new FileInputStream(src).getChannel();
outChannel = new FileOutputStream(dest).getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
} finally {
if (inChannel != null) {
inChannel.close();
}
if (outChannel != null) {
outChannel.close();
}
}
}
/**
* 从classLoader中获取
*
* @param classLoader
*/
private Object getDexElementsByClassLoader(ClassLoader classLoader) throws Exception {
//1.先获取pathList
Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
//所有属性都可以获得
pathListField.setAccessible(true);
Object pathList = pathListField.get(classLoader);
//2.获得pathList中的dexElements
Field dexElementsFiled = pathList.getClass().getDeclaredField("dexElements");
dexElementsFiled.setAccessible(true);
return dexElementsFiled.get(pathList);
}
/**
* 加载全部的修复包
*/
public void loadFixDex() throws Exception {
File[] dexFiles = mDexFile.listFiles();
List<File> fixDexFiles = new ArrayList<>();
for (File dexFile : dexFiles) {
if (dexFile.getName().endsWith(".dex")) {
fixDexFiles.add(dexFile);
}
}
fixDexFiles(fixDexFiles);
}
/**
* 修复dex
*/
private void fixDexFiles(List<File> fixDexFiles) throws Exception {
//1.先获取已运行的dexElement
ClassLoader applicationClassLoader = mContext.getClassLoader();
Object dexElements = getDexElementsByClassLoader(applicationClassLoader);
File optimizedDirectory = new File(mDexFile, "odex");
if (!optimizedDirectory.exists()) {
optimizedDirectory.mkdir();
}
//修复
for (File fixDexFile : fixDexFiles) {
ClassLoader fixDexClassLoader = new BaseDexClassLoader(
fixDexFile.getAbsolutePath(),//dex路径 必须要在应用目录下的dex文件中
optimizedDirectory,//解压路径
null,//.so文件的位置
applicationClassLoader//父classloader
);
Object fixDexElement = getDexElementsByClassLoader(fixDexClassLoader);
//3.把补丁的dexElement插到已经运行好的dexElements的最前面
//合并数组
applicationDexElements = combineArray(dexElements, fixDexElement);
}
//注入到原来的类中applicationClassLoader
injectDexElement(applicationClassLoader, applicationDexElements);
}
}