插件化---类加载(一)

北城以北 提交于 2020-01-09 12:12:25

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

最近在学插件化,看的各种脑阔疼,现在随手写点笔记,方便以后回顾, 有理解不对的地方,请指出

加载类都是通过类加载器ClassLoader来实现的,其实类加载器还有几个,关系就如下

    pathClassLoader可以理解为是加载自己写的activity,如TestActivity extends Activity,这时候在TestActivity中onCreat中直接getClassLoader这时候打印出来就是

pathClassLoader

    DexClassLoader可以理解为是加载系统类的,如android.app.activity下的类

先手绘了张类加载的流程,有点乱,凑活看吧,按照自己理解来画的

这里主要的就是双亲委托机制,好处就是安全,防止重复加载, 首先加载一个类的时候,调用loadClass方法,在loadClass方法中首先调用findloadedclass()方法,从已经加载的类中找,找到就直接返回,找不到就调用parent的loadClass方法,这里的parent不是父类,是上一级的意思,在上一级中依旧是findloadedclass(),通过不断递归,一直找到最上级BootClassLoader,它里面没有findloadedclass后就加载自身的findclass,在BaseDexClassLoader的findclass中通过DexPathList的对象pathList.findclass, 然后进入到DexPathList中, 看到findclass方法中是通过Element[] 的对象 dexElements,对此进行for循环,来findclass,得到的每一个element就对应一个dex文件

下面附上加载插件的代码

public class LoadPluginUtils {
    private final static String apkPath = "/sdcard/test-debug.apk";
    public static void start(Context context){
        try {
            Class<?> dexpathClass = Class.forName("dalvik.system.DexPathList");
            Field dexElements = dexpathClass.getDeclaredField("dexElements");
            dexElements.setAccessible(true);

            Class<?> basedexClass = Class.forName("dalvik.system.BaseDexClassLoader");
            Field pathList = basedexClass.getDeclaredField("pathList");
            pathList.setAccessible(true);

            /**
             * 获取插件中的element数组
             * 第一个参数就是路径,插件放置的路径
             * 第二个参数是 上一级的classLoader ,这里context.getClassLoader()是BootClassloader
             */
            PathClassLoader pathClassLoader = new PathClassLoader(apkPath, context.getClassLoader());
            //获取pathClassLoader对象中pathList的属性
            Object pluginPathList = pathList.get(pathClassLoader);
            //获取pluginPathList对象中dexElements的属性
            Object[] pluginElement = (Object[]) dexElements.get(pluginPathList);

            //获取宿主中element数组
            PathClassLoader classLoader = (PathClassLoader) context.getClassLoader();
            Object hostPathList = pathList.get(classLoader);
            Object[] hostElement = (Object[]) dexElements.get(hostPathList);

            //然後將两个element的数组合并成一个
            Object[] arr = (Object[]) Array.newInstance(hostElement.getClass().getComponentType(), hostElement.length + pluginElement.length);
            System.arraycopy(hostElement,0,arr,0,hostElement.length);
            System.arraycopy(pluginElement,0,arr,hostElement.length,pluginElement.length);

            //然后再将合并后的数组赋值给宿主dexElements 属性
            dexElements.set(hostPathList,arr);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在application中先进项加载,

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        LoadPluginUtils.start(this);
    }
}

然后在调用的地方

Class<?> aClass = Class.forName("com.example.test.Test");
Method print = aClass.getMethod("print");
print.invoke(null);

记得要把插件放到sdcard里面

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!