从jvm到插件化

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到插件化)