简书链接:插件化的新方式把插件apk的elementsField添加到系统的BaseDexClassLoader中去
文章字数:205,阅读全文大约需要1分钟
模板代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 String cachePath = context.getCacheDir().getAbsolutePath();
String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/plugin.apk";
DexClassLoader dexClassLoader = new DexClassLoader(apkPath, cachePath, cachePath, context.getClassLoader());

// 第一步 找到 插件的Elements数组 dexPathlist ----?dexElement

try {
Class myDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
Field myPathListFiled=myDexClazzLoader.getDeclaredField("pathList");
myPathListFiled.setAccessible(true);
Object myPathListObject =myPathListFiled.get(dexClassLoader);


Class myPathClazz=myPathListObject.getClass();
Field myElementsField = myPathClazz.getDeclaredField("dexElements");
myElementsField.setAccessible(true);
// 自己插件的 dexElements[]
Object myElements=myElementsField.get(myPathListObject);

// 第二步 找到 系统的Elements数组 dexElements
PathClassLoader pathClassLoader= (PathClassLoader) context.getClassLoader();
Class baseDexClazzLoader = Class.forName("dalvik.system.BaseDexClassLoader");
Field pathListFiled = baseDexClazzLoader.getDeclaredField("pathList");
pathListFiled.setAccessible(true);
Object pathListObject = pathListFiled.get(pathClassLoader);

Class systemPathClazz = pathListObject.getClass();
Field systemElementsField = systemPathClazz.getDeclaredField("dexElements");
systemElementsField.setAccessible(true);
//系统的 dexElements[]
Object systemElements = systemElementsField.get(pathListObject);
// 第三步 上面的dexElements 数组 合并成新的 dexElements 然后通过反射重新注入系统的Field (dexElements )变量中

// 新的 Element[] 对象
// dalvik.system.Element

int systemLength = Array.getLength(systemElements);
int myLength = Array.getLength(myElements);
// 找到 Element 的Class类型 数组 每一个成员的类型
Class<?> sigleElementClazz = systemElements.getClass().getComponentType();
int newSysteLength = myLength + systemLength;
Object newElementsArray=Array.newInstance(sigleElementClazz, newSysteLength);
//融合
for (int i = 0; i < newSysteLength; i++) {
// 先融合 插件的Elements
if (i < myLength) {
Array.set(newElementsArray, i, Array.get(myElements, i));
}else {
Object value = Array.get(systemElements, i - myLength);
Array.set(newElementsArray,i, value);
}
}
Field elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
elementsField.setAccessible(true);
// 将新生成的EleMents数组对象重新放到系统中去
elementsField.set( pathListObject,newElementsArray);


} catch (Exception e) {
e.printStackTrace();
}





这样的方式就实现了class不需要class.forname()指定插件classloader了。这种方式弊端就是我这个qq机器人是不能这么搞的,插件卸载了我还要手动移除。。。

多累啊。