1.从网络上下载插件然后拷贝到app内部存储,或者直接复制插件到app内部存储。

2.自定义ClassLoader

  String dexopt = context.getDir("dexopt", 0).getAbsolutePath();

        pluginClassLoader = new DexClassLoader(pluginPath, dexopt, null, hostClassLoader);   //hostClassLoader 为宿主app的类加载器,所以自定义类加载器可以加载应用的类。

加载类并调用:

 try {
            //在这里测试是否真正加载了插件中的类
            Class<?> clz = pluginClassLoader.loadClass("com.renxh.pluginapp.PluginUtils");
            Object instance = clz.newInstance();
            Method showToast = clz.getMethod("add", int.class, int.class);
            int invoke = (int) showToast.invoke(instance, 1, 2);
            Log.d("mmm", "插件结果" + invoke);
        } catch (Exception e) {
            e.printStackTrace();
        }

3.将自定义ClassLoader合并到app的ClassLoader

按照https://cloud.tencent.com/developer/article/1071815这篇文章的说法,可以将自定义的classloader与应用的classloader合并。

Field baseDexpathList = null;
        try {
            //获取插件中的类
            baseDexpathList = BaseDexClassLoader.class.getDeclaredField("pathList");
            baseDexpathList.setAccessible(true);
            Object pathlist = baseDexpathList.get(pluginClassLoader);
            Field dexElementsFiled = pathlist.getClass().getDeclaredField("dexElements");
            dexElementsFiled.setAccessible(true);
            Object[] dexElements = (Object[]) dexElementsFiled.get(pathlist);

            //获取应用内的类
            Field baseDexpathList1 = BaseDexClassLoader.class.getDeclaredField("pathList");
            baseDexpathList1.setAccessible(true);
            Object pathlist1 = baseDexpathList1.get(hostClassLoader);
            Field dexElementsFiled1 = pathlist1.getClass().getDeclaredField("dexElements");
            dexElementsFiled1.setAccessible(true);
            Object[] dexElements1 = (Object[]) dexElementsFiled1.get(pathlist1);


            //创建一个数组
            Object[] finalArray = (Object[]) Array.newInstance(dexElements.getClass().getComponentType(),
                    dexElements.length + dexElements1.length);
            //合并插件和应用内的类
            System.arraycopy(dexElements, 0, finalArray, 0, dexElements.length);
            System.arraycopy(dexElements1, 0, finalArray, dexElements.length, dexElements1.length);
            //把新数组替换掉原先的数组
            dexElementsFiled1.set(pathlist1, finalArray);
            Log.d("mmm","插件加载完成");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

但是在android8.0版本以上就会出现适配问题:

https://developer.android.google.cn/about/versions/oreo/android-8.0-changes

Android 8.0 不支持多个类加载器同时尝试使用相同的 DexFile 对象来定义类。尝试进行此操作,会导致 Android 运行时引发 InternalError 错误,同时显示消息“Attempt to register dex file <filename> with multiple class loaders”。

自定义的classloader已经加载了一遍dex,再把dex合并到app的classloader里,就会报上面的错误。

所以再看https://cloud.tencent.com/developer/article/1071815这篇文章的说的单dexClassLoader与多dexClassLoader的区别,所以只能让插件classLoader负责加载自己的类。但是这样做的话,插件里的类可能在某个地方会被系统的classLoader去加载,这时候就会报class not found的错误,比如插件里activity类的加载。


1 条评论

正确的Hook Activity启动的方式 – 修符道人的江湖 · 2021年1月14日 下午10:02

[…] android自定义类加载器 […]

发表回复

您的电子邮箱地址不会被公开。