美文网首页
插件化开发 -- 类加载

插件化开发 -- 类加载

作者: NengLee | 来源:发表于2020-11-28 17:40 被阅读0次

    java文件在编译后会生成一个class文件,而在Android中会将代码编译后生成多个dex文件,在通过zip打包成一个apk,然而通过对apk的解压会发现其中有一个或者多个 xxxclass.dex文件。

    类加载的基本

    1. BootClassLoader:加载SDK 例如FrameWork的层,如Activity.class
    2. PathClassLoader: 应用层的类型 例如业务MainActivty类 & 比如熟悉第三方包框架okHttp也是,加载系统中已经安装过的apk
    3. DexClassLocader:属于定制开发使用,自定义更加方便适配,可以加载jar/apk/dex,可以从SD卡中加载未安装的apk
    ClassLoader结构

    PathClassLoader.java

    public class PathClassLoader extends BaseDexClassLoader {
        
    
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    
         /**
         * @param dexPath:dex路径 其中Apk只能加载系统中已经安装过的apk
         * @param libraryPath:动态库路径
         * @param parent:制定父类加载器,以保证双亲委派机制从而实现每个类只加载一次。
         */
        public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    }
    

    DexClassLocader.java

    public class DexClassLoader extends BaseDexClassLoader {
    
         /**
         * @param dexPath: dex路径 [jar/apk/dex,可以从SD卡中加载未安装的apk ]
         * @param optimizedDirectory: 制定输出的dex优化后odex文件,可以为null
         * @param librarySearchPath: 动态的库路劲,添加到App动态库搜素列表中
         * @param parent: 制定父类加载器,以保证双亲委派机制从而实现每个类只加载一次。
         */
        public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    }
    

    双亲委派机制

    双亲委派机制

    当某个类需要加载器加载时,首先会检测当前类是否加载过findLoadedClass(),如果已经加载了那么直接获取并且返回。

    如果没有加载就会委托给它的上级 ,当parent不为null,则parent.loadClass()进行加载,依次递归

    如果找到了或者是已经加载了就返回,如果没有找到也没加载那么才自己去加载。而这个整个流程就属于双亲委派机制。

    源码分析

      protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
        {
                //检查类是否已经加载
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    try {
                        if (parent != null) {
                            //递归loadClass
                            c = parent.loadClass(name, false);
                        } else {
                             //如果父类的加载器为空 则说明递归到bootStrapClassloader了
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) {
                     //如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
                        c = findClass(name);
                    }
                }
                return c;
        }
    

    通过对双亲委派机制的分析,可以得其作用:

    1. 防止.class被多个加载器所加载,会用到递归询问的方式查询是否加载。
    2. 防止.class不被篡改,而从保证.class的执行安全。

    dex文件的生成指定

    package com.example.myapplication;
    
    import android.util.Log;
    
    public class Test {
    
        public static void logStart() {
            Log.e("Test", "启动logStart");
        }
    }
    

    一个class文件要生成xxx.dex,需要借助dx.bat工具,在Android-sdk下:wind路径 Android/sdk/build-tools/api版本/dx.bat 【记得环境变量】

    • 可以用Gradle-build或者是Javac编译成class文件

    • F:\Android\app\build\intermediates\javac\debug\classes\com\example\myapplication\Test.class

    • dx --dex --output=xxx.dex com/example/myapplication/Test.class

    命令译:dx --dex --output=xxx(生成dex名).dex input.class(目标文件、包含完整包名) 【dx时候cmd要进入到当期com/包名的初始目录】

    控制台

    调用dex中的类

    把生成后的test.dex文件放入Sd根目录,通过DexClassLocader或者是PathClassLoader进行类加载:

    public void onButton(View view) {
        try {
            DexClassLoader dexClassLoader = new DexClassLoader("/sdcard/test.dex",
                    MainActivity.this.getCacheDir().getAbsolutePath(),
                    null,
                    MainActivity.this.getClassLoader());
    
            Class<?> clazz = dexClassLoader.loadClass("com.example.myapplication.Test");
            Method print = clazz.getMethod("logStart");
            print.invoke(null);
    
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    Path 和 Dex 更多细节参考

    相关文章

      网友评论

          本文标题:插件化开发 -- 类加载

          本文链接:https://www.haomeiwen.com/subject/lkkcwktx.html