一、Android虚拟机加载class原理

我们知道Java在运行时加载对应的类是通过ClassLoader来实现的,ClassLoader本身是一个抽象来,Android中使用PathClassLoader类作为Android的默认的类加载器,
PathClassLoader其实实现的就是简单的从文件系统中加载类文件。PathClassLoade本身继承自BaseDexClasoader,BaseDexClassLoader重写了findClass方法。

详细的可以看这篇文章:Android热更新实现原理,本篇文章不做介绍,这里只从源码上讲解multidex的实现原理。

二、源码解析
  1. 首先从Multidex.install方法开始分析,以下是install的核心代码:
    public static void install(Context context) {        Log.i(TAG, "install");        if (IS_VM_MULTIDEX_CAPABLE) {            Log.i(TAG, "VM has multidex support, MultiDex support library is disabled.");            return;        }        if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) {            throw new RuntimeException("Multi dex installation failed. SDK " + Build.VERSION.SDK_INT                + " is unsupported. Min SDK version is " + MIN_SDK_VERSION + ".");        }        try {            ApplicationInfo applicationInfo = getApplicationInfo(context);            if (applicationInfo == null) {                // Looks like running on a test Context, so just return without patching.                return;            }            synchronized (installedApk) {                String apkPath = applicationInfo.sourceDir;                if (installedApk.contains(apkPath)) {                    return;                }                installedApk.add(apkPath);                if (Build.VERSION.SDK_INT > MAX_SUPPORTED_SDK_VERSION) {                        ......                }                ClassLoader loader;                try {                    //这里获取到BaseDexClassLoader的类加载器。                    loader = context.getClassLoader();                } catch (RuntimeException e) {                    return;                }                if (loader == null) {                    return;                }                try {                  clearOldDexDir(context);                } catch (Throwable t) {                                }                File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);                List files = MultiDexExtractor.load(context, applicationInfo, dexDir, false);                if (checkValidZipFiles(files)) {                    //这里加载第二个dex文件                    installSecondaryDexes(loader, dexDir, files);                } else {                    // Try again, but this time force a reload of the zip file.                    files = MultiDexExtractor.load(context, applicationInfo, dexDir, true);                    if (checkValidZipFiles(files)) {                        installSecondaryDexes(loader, dexDir, files);                    } else {                        // Second time didn't work, give up                        throw new RuntimeException("Zip files were not valid.");                    }                }            }        } catch (Exception e) {            Log.e(TAG, "Multidex installation failure", e);            throw new RuntimeException("Multi dex installation failed (" + e.getMessage() + ").");        }        Log.i(TAG, "install done");    }

我们先分析下面这段代码:

    private static final boolean IS_VM_MULTIDEX_CAPABLE = isVMMultidexCapable(System.getProperty("java.vm.version"));    private static final int MIN_SDK_VERSION = 4; //4是android 1.6        public static void install(Context context) {        Log.i(TAG, "install");        //isVMMultidexCapable()方法,判断虚拟机是否支持multidex        if (IS_VM_MULTIDEX_CAPABLE) {            Log.i(TAG, "VM has multidex support, MultiDex support library is disabled.");            return;        }        //在sdk1.6以前是不支持multidex的        if (Build.VERSION.SDK_INT < MIN_SDK_VERSION) {            throw new RuntimeException("Multi dex installation failed. SDK " + Build.VERSION.SDK_INT                    + " is unsupported. Min SDK version is " + MIN_SDK_VERSION + ".");        }        .......    }

我们看下isVMMultidexCapable方法:

    //这里的versionString是 java.vm.version    static boolean isVMMultidexCapable(String versionString) {        boolean isMultidexCapable = false;        if (versionString != null) {            Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);            if (matcher.matches()) {                try {                    int major = Integer.parseInt(matcher.group(1));                    int minor = Integer.parseInt(matcher.group(2));                    isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)                            || ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)                                    && (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));                } catch (NumberFormatException e) {                    // let isMultidexCapable be false                }            }        }        Log.i(TAG, "VM with version " + versionString +                (isMultidexCapable ?                        " has multidex support" :                        " does not have multidex support"));        return isMultidexCapable;    }

getProperty()的参数:

mean name example
java.vm.version VM implementation version 1.2.0

这里主要判断虚拟机是否已经支持multidex,如不支持就直接返回。

继续分析,经过一系列的判断,验证等步骤,最终会执行installSecondaryDexes方法,这个方法才是加载我们拆分的dex文件:

    private static void installSecondaryDexes(ClassLoader loader, File dexDir, List files)            throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException,            InvocationTargetException, NoSuchMethodException, IOException {        if (!files.isEmpty()) {            if (Build.VERSION.SDK_INT >= 19) {                V19.install(loader, files, dexDir);            } else if (Build.VERSION.SDK_INT >= 14) {                V14.install(loader, files, dexDir);            } else {                V4.install(loader, files);            }        }    }

首先判断,当前的sdk版本,有3个不同的方法,分别是V19 V14 V4,我们分析下V19.install方法

        private static void install(ClassLoader loader, List additionalClassPathEntries,                File optimizedDirectory)                        throws IllegalArgumentException, IllegalAccessException,                        NoSuchFieldException, InvocationTargetException, NoSuchMethodException {            /* The patched class loader is expected to be a descendant of             * dalvik.system.BaseDexClassLoader. We modify its             * dalvik.system.DexPathList pathList field to append additional DEX             * file entries.             */             //这里通过反射获取BaseClassLoader中的变量名为”pathList“ Field            Field pathListField = findField(loader, "pathList");            //这里拿到pathList这个对象            Object dexPathList = pathListField.get(loader);            ArrayList suppressedExceptions = new ArrayList();            //关键步骤,反射调用makeDexElements函数,            expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList,                    new ArrayList(additionalClassPathEntries), optimizedDirectory,                    suppressedExceptions));            if (suppressedExceptions.size() > 0) {                for (IOException e : suppressedExceptions) {                    Log.w(TAG, "Exception in makeDexElement", e);                }                Field suppressedExceptionsField =                        findField(loader, "dexElementsSuppressedExceptions");                IOException[] dexElementsSuppressedExceptions =                        (IOException[]) suppressedExceptionsField.get(loader);                if (dexElementsSuppressedExceptions == null) {                    dexElementsSuppressedExceptions =                            suppressedExceptions.toArray(                                    new IOException[suppressedExceptions.size()]);                } else {                    IOException[] combined =                            new IOException[suppressedExceptions.size() +                                            dexElementsSuppressedExceptions.length];                    suppressedExceptions.toArray(combined);                    System.arraycopy(dexElementsSuppressedExceptions, 0, combined,                            suppressedExceptions.size(), dexElementsSuppressedExceptions.length);                    dexElementsSuppressedExceptions = combined;                }                suppressedExceptionsField.set(loader, dexElementsSuppressedExceptions);            }        }

这里的pathDexList是BaseClassLoader下面的一个成员变量,它的类型是DexPathList,这个类主要负责加载Dex并重组;接着看下expandFieldArray的源码,这个函数主要的目的是,先通过反射拿到名为dexElements的field,这个dexElements就是我们刚刚说的Elements数组,然后将刚刚makeDexElements生成的Elements数组放在dexElements的首部,makeDexElements生成的数组就是加载Dex文件的数组,这样就完成了将dex的class放在ClassLoader前面的功能。

    /**     * Replace the value of a field containing a non null array, by a new array containing the     * elements of the original array plus the elements of extraElements.     * @param instance the instance whose field is to be modified.     * @param fieldName the field to modify.     * @param extraElements elements to append at the end of the array.     */    private static void expandFieldArray(Object instance, String fieldName,            Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,            IllegalAccessException {        Field jlrField = findField(instance, fieldName);        Object[] original = (Object[]) jlrField.get(instance);        Object[] combined = (Object[]) Array.newInstance(                original.getClass().getComponentType(), original.length + extraElements.length);        System.arraycopy(original, 0, combined, 0, original.length);        System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);        jlrField.set(instance, combined);    }

最后分析下makeDexElements这个函数,首先调用的是V19下面的makeDexElements函数:

        /**         * A wrapper around         * {@code private static final dalvik.system.DexPathList#makeDexElements}.         */        private static Object[] makeDexElements(                Object dexPathList, ArrayList files, File optimizedDirectory,                ArrayList suppressedExceptions)                        throws IllegalAccessException, InvocationTargetException,                        NoSuchMethodException {            Method makeDexElements =                    findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class,                            ArrayList.class);            return (Object[]) makeDexElements.invoke(dexPathList, files, optimizedDirectory,                    suppressedExceptions);        }

事实上,也是通过反射调用DexPathList里面的makeDexElements方法,具体就不做分析。

    private static Element[] makeDexElements(ArrayList files, File optimizedDirectory, ArrayList suppressedExceptions) {        ArrayList elements = new ArrayList();        for (File file : files) {            File zip = null;            DexFile dex = null;            String name = file.getName();            if (file.isDirectory()) {                elements.add(new Element(file, true, null, null));            } else if (file.isFile()){                if (name.endsWith(DEX_SUFFIX)) {                    try {                        dex = loadDexFile(file, optimizedDirectory);                    } catch (IOException ex) {                        System.logE("Unable to load dex file: " + file, ex);                    }                } else {                    zip = file;                    try {                        dex = loadDexFile(file, optimizedDirectory);                    } catch (IOException suppressed) {                        suppressedExceptions.add(suppressed);                    }                }            } else {                System.logW("ClassLoader referenced unknown path: " + file);            }            if ((zip != null) || (dex != null)) {                elements.add(new Element(file, false, zip, dex));            }        }        return elements.toArray(new Element[elements.size()]);    }

更多相关文章

  1. Android(安卓)多媒体应用——MediaRecorder录制音频
  2. 2018-08-07
  3. AndroidStudioTip--用Builder模式替代构造方法
  4. android 图形系统requestLayout的流程
  5. 【Android(安卓)Developers Training】 104. 接受地点更新
  6. Android解决加载大图片时内存溢出的问题
  7. popupwindow的一些注意事项
  8. android方法数超过64k和Gradle编译OOM解决方法
  9. Android(安卓)Vendor Test Suite (VTS) 的概念、作用及测试方法

随机推荐

  1. 日记月累:Android SDK Manager无法更新
  2. android so文件是什么
  3. android 玩转ContentProvider之二--实现多
  4. android实现在横竖屏切换时页面信息不被
  5. android UI进阶之弹窗的使用
  6. 巨人的崛起 Android操作系统发展历程
  7. Android系统的Binder机制分析
  8. android TextView设置字过多长长度后面显
  9. Android编译环境搭建步骤(公司) - 写给自
  10. android之Binder机制解析