【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
最近在学插件化,看的各种脑阔疼,现在随手写点笔记,方便以后回顾, 有理解不对的地方,请指出
加载类都是通过类加载器ClassLoader来实现的,其实类加载器还有几个,关系就如下
pathClassLoader可以理解为是加载自己写的activity,如TestActivity extends Activity,这时候在TestActivity中onCreat中直接getClassLoader这时候打印出来就是
pathClassLoader
DexClassLoader可以理解为是加载系统类的,如android.app.activity下的类
先手绘了张类加载的流程,有点乱,凑活看吧,按照自己理解来画的
这里主要的就是双亲委托机制,好处就是安全,防止重复加载, 首先加载一个类的时候,调用loadClass方法,在loadClass方法中首先调用findloadedclass()方法,从已经加载的类中找,找到就直接返回,找不到就调用parent的loadClass方法,这里的parent不是父类,是上一级的意思,在上一级中依旧是findloadedclass(),通过不断递归,一直找到最上级BootClassLoader,它里面没有findloadedclass后就加载自身的findclass,在BaseDexClassLoader的findclass中通过DexPathList的对象pathList.findclass, 然后进入到DexPathList中, 看到findclass方法中是通过Element[] 的对象 dexElements,对此进行for循环,来findclass,得到的每一个element就对应一个dex文件
下面附上加载插件的代码
public class LoadPluginUtils {
private final static String apkPath = "/sdcard/test-debug.apk";
public static void start(Context context){
try {
Class<?> dexpathClass = Class.forName("dalvik.system.DexPathList");
Field dexElements = dexpathClass.getDeclaredField("dexElements");
dexElements.setAccessible(true);
Class<?> basedexClass = Class.forName("dalvik.system.BaseDexClassLoader");
Field pathList = basedexClass.getDeclaredField("pathList");
pathList.setAccessible(true);
/**
* 获取插件中的element数组
* 第一个参数就是路径,插件放置的路径
* 第二个参数是 上一级的classLoader ,这里context.getClassLoader()是BootClassloader
*/
PathClassLoader pathClassLoader = new PathClassLoader(apkPath, context.getClassLoader());
//获取pathClassLoader对象中pathList的属性
Object pluginPathList = pathList.get(pathClassLoader);
//获取pluginPathList对象中dexElements的属性
Object[] pluginElement = (Object[]) dexElements.get(pluginPathList);
//获取宿主中element数组
PathClassLoader classLoader = (PathClassLoader) context.getClassLoader();
Object hostPathList = pathList.get(classLoader);
Object[] hostElement = (Object[]) dexElements.get(hostPathList);
//然後將两个element的数组合并成一个
Object[] arr = (Object[]) Array.newInstance(hostElement.getClass().getComponentType(), hostElement.length + pluginElement.length);
System.arraycopy(hostElement,0,arr,0,hostElement.length);
System.arraycopy(pluginElement,0,arr,hostElement.length,pluginElement.length);
//然后再将合并后的数组赋值给宿主dexElements 属性
dexElements.set(hostPathList,arr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在application中先进项加载,
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LoadPluginUtils.start(this);
}
}
然后在调用的地方
Class<?> aClass = Class.forName("com.example.test.Test"); Method print = aClass.getMethod("print"); print.invoke(null);
记得要把插件放到sdcard里面
来源:oschina
链接:https://my.oschina.net/fbf8866/blog/3155211