jvm,是java能够跨平台的根本原因,C语言所用到的是纯二进制字节码,而java通过javac编译而成的是class字节码文件,jvm就是起到字节码转换的作用。
类加载过程:
jvm解析所编译成的class字节码文件,进行加载过程,其中采用双亲委派的模式,各个加载器都是先委托父类加载器加载类,若确实没加载到自己再加载,这样实现可以加载复用,
加载的顺序是:
1.首先初始化父类的static变量和块,按出现顺序
2.初始化子类的static变量和块,按出现顺序
3.初始化父类的普通变量,调用父类的构造函数
4.初始化子类的普通变量,调用子类的构造函数
因为类加载器加载的是已经编译好的class文件,所以面对需要热替换的概念,有个方案是自定义类加载器。
自定义类加载:
先看看jdk类加载源码:
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
先看.class是否加载过,没有并且有父类加载父类的加载方法,父类加载的为null就让子类加载(findBootstrapClass0),一直递归。当子类都无法加载就执行findClass。
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
这里没有具体实现,可以开发者重写
自定义类加载器实现:
1、如果不想打破双亲委派模型,那么只需要重写findClass方法即可
2、如果想打破双亲委派模型,那么就重写整个loadClass方法
public class MyClassLoader extends ClassLoader
{
public MyClassLoader()
{
}
public MyClassLoader(ClassLoader parent)
{
super(parent);
}
protected Class<?> findClass(String name) throws ClassNotFoundException
{
File file = getClassFile(name);
try
{
byte[] bytes = getClassBytes(file);
Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
return c;
}
catch (Exception e)
{
e.printStackTrace();
}
return super.findClass(name);
}
private File getClassFile(String name){
File file = new File("D:/Person.class");
return file;
}
private byte[] getClassBytes(File file) throws Exception
{
// 这里要读入.class的字节,因此要使用字节流
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(baos);
ByteBuffer by = ByteBuffer.allocate(1024);
while (true)
{
int i = fc.read(by);
if (i == 0 || i == -1)
break;
by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
}
public static void main(String[] args) throws Exception
{
MyClassLoader mcl = new MyClassLoader();
Class<?> c1 = Class.forName("com.xrq.classloader.Person", true, mcl);
Object obj = c1.newInstance();
System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());
}
针对Android 65530的问题,谷歌提出的MultiDex自动拆包机制,就利用自定义类加载器,在BaseDexClassLoader里,重写了findClass方法,同时可以在gradle文件配置代码的分包,把开机启动就需要的代码都放到Classes.dex中。在 Application中执行MultiDex.install。(不过也可以通过arr解决)
从自定义类加载器到dex动态的动态加载:
DexClassLoader和PathClassLoader这两个动态加载dex类,支持动态加载文件中的dex文件,DexClassLoader和PathClassLoader,DexClassLoader可加载jar/apk/dex,且支持从SD卡加载;PathClassLoader据说只能加载已经安装在Android系统内APK文件。
过程就是将编写好的代码,打包成jar文件,然后通过dx命令生成dex文件,在项目中启动的时候就去动态加载这个dex文件,如果需要改动,再在本地生成dex文件,去替换项目中的dex文件即可。既然是通过动态加载dex文件有个弊端就是没办法加载res文件,除非是代码生成ui,不然UI更新不了。
另外两种插件化思路:
1、主流思想React Nativie
RN 这套框架让 JS开发者可以大部分使用JS代码就可以构建一个跨平台APP,最主要的就是实现了一套JAVA native和 JS通信的方案,js文件可放到服务器也可放在本地,推送更新。
2、luaView
实现lua脚本跟java native之间的通信。将lua文件存在在raw或者asset文件中动态更新。
类加载过程:
jvm解析所编译成的class字节码文件,进行加载过程,其中采用双亲委派的模式,各个加载器都是先委托父类加载器加载类,若确实没加载到自己再加载,这样实现可以加载复用,
加载的顺序是:
1.首先初始化父类的static变量和块,按出现顺序
2.初始化子类的static变量和块,按出现顺序
3.初始化父类的普通变量,调用父类的构造函数
4.初始化子类的普通变量,调用子类的构造函数
因为类加载器加载的是已经编译好的class文件,所以面对需要热替换的概念,有个方案是自定义类加载器。
自定义类加载:
先看看jdk类加载源码:
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
先看.class是否加载过,没有并且有父类加载父类的加载方法,父类加载的为null就让子类加载(findBootstrapClass0),一直递归。当子类都无法加载就执行findClass。
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
这里没有具体实现,可以开发者重写
自定义类加载器实现:
1、如果不想打破双亲委派模型,那么只需要重写findClass方法即可
2、如果想打破双亲委派模型,那么就重写整个loadClass方法
public class MyClassLoader extends ClassLoader
{
public MyClassLoader()
{
}
public MyClassLoader(ClassLoader parent)
{
super(parent);
}
protected Class<?> findClass(String name) throws ClassNotFoundException
{
File file = getClassFile(name);
try
{
byte[] bytes = getClassBytes(file);
Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
return c;
}
catch (Exception e)
{
e.printStackTrace();
}
return super.findClass(name);
}
private File getClassFile(String name){
File file = new File("D:/Person.class");
return file;
}
private byte[] getClassBytes(File file) throws Exception
{
// 这里要读入.class的字节,因此要使用字节流
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(baos);
ByteBuffer by = ByteBuffer.allocate(1024);
while (true)
{
int i = fc.read(by);
if (i == 0 || i == -1)
break;
by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
}
public static void main(String[] args) throws Exception
{
MyClassLoader mcl = new MyClassLoader();
Class<?> c1 = Class.forName("com.xrq.classloader.Person", true, mcl);
Object obj = c1.newInstance();
System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());
}
针对Android 65530的问题,谷歌提出的MultiDex自动拆包机制,就利用自定义类加载器,在BaseDexClassLoader里,重写了findClass方法,同时可以在gradle文件配置代码的分包,把开机启动就需要的代码都放到Classes.dex中。在 Application中执行MultiDex.install。(不过也可以通过arr解决)
从自定义类加载器到dex动态的动态加载:
DexClassLoader和PathClassLoader这两个动态加载dex类,支持动态加载文件中的dex文件,DexClassLoader和PathClassLoader,DexClassLoader可加载jar/apk/dex,且支持从SD卡加载;PathClassLoader据说只能加载已经安装在Android系统内APK文件。
过程就是将编写好的代码,打包成jar文件,然后通过dx命令生成dex文件,在项目中启动的时候就去动态加载这个dex文件,如果需要改动,再在本地生成dex文件,去替换项目中的dex文件即可。既然是通过动态加载dex文件有个弊端就是没办法加载res文件,除非是代码生成ui,不然UI更新不了。
另外两种插件化思路:
1、主流思想React Nativie
RN 这套框架让 JS开发者可以大部分使用JS代码就可以构建一个跨平台APP,最主要的就是实现了一套JAVA native和 JS通信的方案,js文件可放到服务器也可放在本地,推送更新。
2、luaView
实现lua脚本跟java native之间的通信。将lua文件存在在raw或者asset文件中动态更新。