Android中的动态加载机制能更好的优化我们的应用,同时实现动态的更新,这就便于我们管理我们的应用,通过插件化来减轻我们的内存以及CPU消耗,在不发布新版本的情况下能更新某些模块。
当然这里要说的并不是android中的动态加载机制,而是java中的ClassLoader动态加载我们的class,虽然android是基于Dalvik,但是先了解java中JVM怎么来加载我们的class的对于我们以后了解Android中的动态加载机制会有很大的帮助。
于是乎在网上查阅了很多关于类加载的文章了解JVM是如何通过ClassLoader来加载我们的class的。
java为我们提供了3个不同的ClassLoadr,分别是:
1.BootstrapClassLoader---主要负责加载java中的核心类库。是用C++代码实现的,在java虚拟机启动后初始化。
2.ExtensionClassLoader---主要负责加载java中的扩展类库。
3.AppClassLoader---用于java我们应用下的classpath中的class以及我们的jar。
JVM通过这些ClassLoadr把我们的class转换成字节码文件存贮到内存中,然后创建一个Class用特定的数据结构来封装他们,接着我们就能通过这个Class来访问其中的方法以及变量了。(这不就是我们的反射机制么,通过Class对象来获取其中的方法或者变量),当然JVM还有个验证的过程,只有通过javac编译来的class才能被ClassLoader所加载。
而ClassLoader是通过双亲委托模式来加载我们的class,就是先通过父类的ClassLoader来加载我们的class,如果父类加载失败,则通过我们的子ClassLoader来加载我们的class。(这里的父类与子类并不是集成关系)
我们可以通过URLClassLoader来动态加载我们的class也可以通过继承ClassLoader来实现我们的动态加载。而我们的ExtensionClassLoader与AppClassLoader都是继承自URLClassLoader。我这里是通过继承一个ClassLoader来实现的,通过获取Class的字节码文件来生成我们的Class,然后通过反射机制来调用我们类中的方法。
ClassLoader的扩展方法介绍:
loadClass 通过指定的二进制名来加载类(这里要把我们的包路径传过去,例如"com.ljx.test.Test")
defineClass 将byte数据转换成我们的Class类的实例
findloadClass 如果某个类加载器已经加载过这个class,则返回该类
…..还有很多这里就不一一例举了。
这就是我们的Test的代码
public class MyClassLoader extends ClassLoader {
private byte[] results;
public MyClassLoader(String pathName) {
//拿到class转成的字节码文件
results = loadClassFile(pathName);
}
public static void main(String[] args) {
//初始化我们的classloader,同时拿到class所转成的字节码文件
MyClassLoader classLoader = new MyClassLoader("F:\\Test.class");
try {
//这里要把包路径传入进去
Class<?> clazz = classLoader.loadClass("com.ljx.yyy.Test");
Object o = clazz.newInstance();
//通过反射机制调用我们的Test.java中的printToString方法
Method method = clazz.getMethod("printToString");
method.invoke(clazz.newInstance());
System.out.println(o.getClass().getClassLoader().toString());
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
//获取类中的方法名字
String methodName = methods[i].getName();
System.out.println("MethodName : " + methodName);
Class<?>[] params = methods[i].getParameterTypes();
for (int j = 0; j < params.length; j++) {
//获取方法中的参数类型
System.out.println("ParamsType : " + params[j].toString());
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//把我们的class文件转成字节码,用于classloader动态加载
private byte[] loadClassFile(String classPath) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
FileInputStream fi = new FileInputStream(classPath);
BufferedInputStream bis = new BufferedInputStream(fi);
byte[] data = new byte[1024 * 256];
int ch = 0;
while ((ch = bis.read(data, 0, data.length)) != -1) {
bos.write(data, 0, ch);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bos.toByteArray();
}
@Override
protected Class<?> loadClass(String arg0, boolean arg1)
throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(arg0);
if (clazz == null) {
if (getParent() != null) {
try {
//这里我们要用父加载器加载如果加载不成功会抛异常
clazz = getParent().loadClass(arg0);
} catch (Exception e) {
//我们自定义的类加载器的父类 sun.misc.Launcher$AppClassLoader@c387f44
System.out.println("getParent : " + getParent());
//父类的父类 sun.misc.Launcher$ExtClassLoader@659e0bfd
System.out.println("getParent.getparent : " + getParent().getParent());
//父类的父类的父类 为null 也就是我们的Bootstrap ClassLoader 因为它是JVM生成的由C++实现;
//所以拿到的是空
System.out.println("getParent.getparent.getparent : " + getParent().getParent().getParent());
System.out.println("父类ClassLoader加载失败!");
}
}
if (clazz == null) {
clazz = defineClass(arg0, results, 0, results.length);
}
}
return clazz;
}
}
先是获取我们在F:Test.class的字节码,然后通过它来得到我们的class,然后我们就能通过这个class来使用Test这个类中的方法,以下是执行main方法之后的结果:
这样,基本上就实现了从本地加载一个class到我们的项目中,是不是感觉很神奇,通过这种方式我们就能动态的加载不在我们项目中的类,例如从网上获取class的byte来动态更新我们的功能模块,或者动态加载jar中的class来实现我们要实现的功能。
代码中很大一部分是参照了网上的一些例子,当然最主要的还是为了要阐述如何通过ClassLoader来动态加载我们需要加载的类,通过ClassLoader来更好的优化我们的应用。了解了JVM如何来加载class能更好的便于我们理解Android中的动态加载技术;由于技术有限,如果上述有不正确的地方希望见谅。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章