插件化开发 -- 类加载

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 更多细节参考

你可能感兴趣的:(插件化开发 -- 类加载)