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

插件化开发 -- 加载APK

作者: NengLee | 来源:发表于2020-12-05 19:24 被阅读0次

    在App项目中需要加载sd里面的一个APK,显而易见就是调用插件方法。

    需要先了解Android的Dalvik/ART虚拟机,和java的JVM虚拟机几乎一样的标准,了解类加载的流程,通过反射Hook启动插件类。把插件的dex加到主dex里面中,从而调用。

    类加载时序图

    关注Android的类加载时序图,其中 DexPathLisElement 以及 DexFile

    类加载的时序图

    核心流程通过:

    1. BaseDexClassLoader --> findClass
    2. DexPathList -for- findClass || findClass
    3. DexFile --> loadClassBinaryName

    核心源码

    BaseDexClassLoader.源码

    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
            Class c = pathList.findClass(name, suppressedExceptions);
            if (c == null) {
                ClassNotFoundException cnfe = new ClassNotFoundException(
                        "Didn't find class \"" + name + "\" on path: " + pathList);
                for (Throwable t : suppressedExceptions) {
                    cnfe.addSuppressed(t);
                }
                throw cnfe;
            }
            return c;
        }
    

    DexPathList.源码

    /**
         * Finds the named class in one of the dex files pointed at by
         * this instance. This will find the one in the earliest listed
         * path element. If the class is found but has not yet been
         * defined, then this method will define it in the defining
         * context that this instance was constructed with.
         *
         * @param name of class to find
         * @param suppressed exceptions encountered whilst finding the class
         * @return the named class or {@code null} if the class is not
         * found in any of the dex files
         */
        public Class<?> findClass(String name, List<Throwable> suppressed) {
            for (Element element : dexElements) {
                Class<?> clazz = element.findClass(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
    
            if (dexElementsSuppressedExceptions != null) {
                suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
            }
            return null;
        }
    
    
    
        public Class<?> findClass(String name, ClassLoader definingContext,List<Throwable> suppressed) {
                return dexFile != null ?
                        dexFile.loadClassBinaryName(name, definingContext, suppressed)
                        : null;
        }
    

    步骤

    目标:宿主dexElements = 宿主dexElements + 插件dexElements

    1. 获取宿主dexElements
    2. 获取插件dexElements
    3. 合并两个dexElements
    4. 将新的dexElements 赋值到 宿主dexElements
    package com.example.myapplication;
    
    import android.content.Context;
    
    import java.lang.reflect.Array;
    import java.lang.reflect.Field;
    
    import dalvik.system.DexClassLoader;
    
    public class LoadUtil {
        
        //存放apk的路径
        private final static String apkPath = "/sdcard/dxpak-debug.apk";
    
        public static void loadClass(Context context) {
            /**
             * 宿主dexElements = 宿主dexElements + 插件dexElements
             *
             * 获取的是宿主的类加载器  --- 反射 dexElements  宿主
             *
             * 获取的是插件的类加载器  --- 反射 dexElements  插件
             */
            try {
                Class<?> clazz = Class.forName("dalvik.system.BaseDexClassLoader");
                Field pathListField = clazz.getDeclaredField("pathList");
                pathListField.setAccessible(true);
    
                Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList");
                Field dexElementsField = dexPathListClass.getDeclaredField("dexElements");
                dexElementsField.setAccessible(true);
    
                // 1.宿主的 类加载器
                ClassLoader pathClassLoader = context.getClassLoader();
                // DexPathList类的对象
                Object hostPathList = pathListField.get(pathClassLoader);
                // 宿主的 dexElements
                Object[] hostDexElements = (Object[]) dexElementsField.get(hostPathList);
    
    
                // 2.插件的 类加载器
                ClassLoader dexClassLoader = new DexClassLoader(apkPath, context.getCacheDir().getAbsolutePath(),
                        null, pathClassLoader);
                // DexPathList类的对象
                Object pluginPathList = pathListField.get(dexClassLoader);
                // 插件的 dexElements
                Object[] pluginDexElements = (Object[]) dexElementsField.get(pluginPathList);
    
    
                // 3.宿主dexElements = 宿主dexElements + 插件dexElements
                // 创建一个新数组
                Object[] newDexElements = (Object[]) Array.newInstance(hostDexElements.getClass().getComponentType(),
                        hostDexElements.length + pluginDexElements.length);
    
                System.arraycopy(hostDexElements, 0, newDexElements,
                        0, hostDexElements.length);
                System.arraycopy(pluginDexElements, 0, newDexElements,
                        hostDexElements.length, pluginDexElements.length);
    
                // 4.赋值
                dexElementsField.set(hostPathList, newDexElements);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    }
    

    加载调用:

    public void onButtonApk(View view) {
        try {
            Class<?> clazz = Class.forName("com.example.dxpak.Test");
            Method print = clazz.getMethod("loginStar");
            print.invoke(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    相关文章

      网友评论

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

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