1.从网络上下载插件然后拷贝到app内部存储,或者直接复制插件到app内部存储。
2.自定义ClassLoader
String dexopt = context.getDir("dexopt", 0).getAbsolutePath();
pluginClassLoader = new DexClassLoader(pluginPath, dexopt, null, hostClassLoader); //hostClassLoader 为宿主app的类加载器,所以自定义类加载器可以加载应用的类。
加载类并调用:
try {
//在这里测试是否真正加载了插件中的类
Class<?> clz = pluginClassLoader.loadClass("com.renxh.pluginapp.PluginUtils");
Object instance = clz.newInstance();
Method showToast = clz.getMethod("add", int.class, int.class);
int invoke = (int) showToast.invoke(instance, 1, 2);
Log.d("mmm", "插件结果" + invoke);
} catch (Exception e) {
e.printStackTrace();
}
3.将自定义ClassLoader合并到app的ClassLoader
按照https://cloud.tencent.com/developer/article/1071815这篇文章的说法,可以将自定义的classloader与应用的classloader合并。
Field baseDexpathList = null;
try {
//获取插件中的类
baseDexpathList = BaseDexClassLoader.class.getDeclaredField("pathList");
baseDexpathList.setAccessible(true);
Object pathlist = baseDexpathList.get(pluginClassLoader);
Field dexElementsFiled = pathlist.getClass().getDeclaredField("dexElements");
dexElementsFiled.setAccessible(true);
Object[] dexElements = (Object[]) dexElementsFiled.get(pathlist);
//获取应用内的类
Field baseDexpathList1 = BaseDexClassLoader.class.getDeclaredField("pathList");
baseDexpathList1.setAccessible(true);
Object pathlist1 = baseDexpathList1.get(hostClassLoader);
Field dexElementsFiled1 = pathlist1.getClass().getDeclaredField("dexElements");
dexElementsFiled1.setAccessible(true);
Object[] dexElements1 = (Object[]) dexElementsFiled1.get(pathlist1);
//创建一个数组
Object[] finalArray = (Object[]) Array.newInstance(dexElements.getClass().getComponentType(),
dexElements.length + dexElements1.length);
//合并插件和应用内的类
System.arraycopy(dexElements, 0, finalArray, 0, dexElements.length);
System.arraycopy(dexElements1, 0, finalArray, dexElements.length, dexElements1.length);
//把新数组替换掉原先的数组
dexElementsFiled1.set(pathlist1, finalArray);
Log.d("mmm","插件加载完成");
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
但是在android8.0版本以上就会出现适配问题:
https://developer.android.google.cn/about/versions/oreo/android-8.0-changes
Android 8.0 不支持多个类加载器同时尝试使用相同的 DexFile 对象来定义类。尝试进行此操作,会导致 Android 运行时引发 InternalError
错误,同时显示消息“Attempt to register dex file <filename>
with multiple class loaders”。
自定义的classloader已经加载了一遍dex,再把dex合并到app的classloader里,就会报上面的错误。
所以再看https://cloud.tencent.com/developer/article/1071815这篇文章的说的单dexClassLoader与多dexClassLoader的区别,所以只能让插件classLoader负责加载自己的类。但是这样做的话,插件里的类可能在某个地方会被系统的classLoader去加载,这时候就会报class not found的错误,比如插件里activity类的加载。
1 条评论
正确的Hook Activity启动的方式 – 修符道人的江湖 · 2021年1月14日 下午10:02
[…] android自定义类加载器 […]