Android黑科技动态加载(一)之Java中的ClassLoader
目录
Android黑科技动态加载(一)之Java中的ClassLoader
Android黑科技动态加载(二)之Android中的ClassLoader
Android黑科技动态加载(三)之动态加载资源
Android黑科技动态加载(四)之插件化开发
项目地址
我们的认识
我们都知道, 刚写好的Java的源文件是以
.java
为扩展名的, 需要让JVM
去解析的时候, 必须编译成Java字节码
, 而Java字节码文件就是以.class
为扩展名的.ClassLoader
的对象就是去加载这些Java字节码
文件
Java默认的ClassLoader
在Java默认环境中, 提供了三种Classloader
BootStrap ClassLoader
:启动类加载器
,Java类加载器中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar
、resources.jar
、charsets.jar
等Extension ClassLoader
:扩展类加载器
,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/
目下的所有jarApp ClassLoader
:系统类加载器
,负责加载应用程序classpath
目录下的所有jar和class文件
有兴趣的朋友可以通过
Class.getClassLoader()
去验证一下.
ClassLoader加载原理
加载顺序(双亲委托机制)
如果我们
自定义一个ClassLoader
叫做MyClassLoader
, 当查找需要的类的时候,MyClassLoader
会把该责任委托给它的父类加载器
不是通过继承的父类, 是通过parent属性去持有. 整个过程从顶到下, 如果一个类找不到, 那么查找顺序是BootStrap ClassLoader
->Extension ClassLoader
->App ClassLoader
->MyClassLoader
->抛出异常ClassNotFoundException
两个类是否相等
一般我们认为两个类是否相等, 仅仅通过
包名+类名
去区分. 事实上还有一个条件, 就是加载该类的ClassLoader是否是同一个
两个关键方法
ClassLoader中认为比较关键的方法有三个:
public Class<?> loadClass(String name)
: 根据双亲委托机制
的方式查找classprotected Class<?> findClass(String name)
: 根据该ClassLoader的方式去查找classprotected final Class<?> defineClass()
: 该方法有多个重载, 具体参数不写了. 主要作用是把类加载到内存中
loadClass
为一个模板方法, 有兴趣的话可以去看看源码. 所以一般我们只需要重写findClass
即可.
上面三个方法有关系的, loadClass->findClass->defineClass
. loadClass
使用双亲委托机制去查找要加载的类, 当上层ClassLoader不能加载该类时, 就会去使用自己的findClass
去加载类. 加载到的类(可能为空)会传入defineClass
中, 如果为空则抛出异常.
自定义ClassLoader
我们下面自定义一个
MyClassLoader
去加载外部的RemoteClass.class
文件
RemoteClass.class
package top.august1996.demo;public class RemoteClass { public void catched() { System.out.println("I am catched..."); }}
我们把上面文件移到任意目录(
/Users/August/Desktop
)
cd /Users/August/Desktopjavac RemoteClass.java
现在我们就在桌面上编译了一个字节码文件
MyClassLoader.java
package top.august1996.demo;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;public class MyClassLoader extends ClassLoader { private String mClassPath; // Class存放的的目录 public MyClassLoader(String classPath) { mClassPath = classPath; } @Override protected Class<?> findClass(String name) { File clsFile = new File(mClassPath, getClassName(name)); FileInputStream fis = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] b = null; try { fis = new FileInputStream(clsFile); int data = 0; while ((data = fis.read()) != -1) { baos.write(data); } b = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (baos != null) { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } } // 上面是基本的数据流操作, 把Class文件转换成二进制流 return defineClass(name, b, 0, b.length); } /** * 获取Class的完整文件名 * * @param name * @return */ private String getClassName(String name) { String clsName = null; if (name != null) { int lastIndexOf = name.lastIndexOf("."); if (lastIndexOf == -1) { clsName = name + ".class"; } else { clsName = name.substring(lastIndexOf + 1) + ".class"; } } return clsName; }}
TestDemo.java
package top.august1996.demo;import java.lang.reflect.Method;public class TestDemo { public static void main(String[] args) throws Exception { MyClassLoader classLoader = new MyClassLoader("/Users/August/Desktop"); Class<?> cls = classLoader.findClass("top.august1996.demo.RemoteClass"); Object object = cls.newInstance(); Method method = cls.getDeclaredMethod("catched", null); method.invoke(object, null); }}
I am catched...
我们使用反射区调用自己加载的
RemoteClass
的catched
方法, 可以看到结果已经是成功的了.
更多相关文章
- [置顶] Android中View的加载过程
- Android 之采用execSQL和rawQuery方法完成数据的添删改查操作
- Android ListView 滚动的N种方法
- Android sdk manager无法启动之swt.jar文件没有指定
- 关于AVD启动失败的问题--找不到文件篇
- Android解决输入法自动弹出方法大全