public static void install(Context context) { Log.i(TAG, "install"); // 检查当前系统是否支持multidex if (IS_VM_MULTIDEX_CAPABLE) { Log.i(TAG, "VM has multidex support, MultiDex support library is disabled."); try { clearOldDexDir(context); } catch (Throwable t) { Log.w(TAG, "Something went wrong when trying to clear old MultiDex extraction, " + "continuing without cleaning.", t); } return; } // MultiDex最低只支持到1.6 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) { // sourceDir对应于/data/app/<package-name>.apk String apkPath = applicationInfo.sourceDir; // 若给定apk已经install过,直接退出 if (installedApk.contains(apkPath)) { return; } installedApk.add(apkPath); // MultiDex 最高只支持到20(Android 4.4W),更高的版本不能保证正常工作 if (Build.VERSION.SDK_INT > MAX_SUPPORTED_SDK_VERSION) { Log.w(TAG, "MultiDex is not guaranteed to work in SDK version " + Build.VERSION.SDK_INT + ": SDK version higher than " + MAX_SUPPORTED_SDK_VERSION + " should be backed by " + "runtime with built-in multidex capabilty but it's not the " + "case here: java.vm.version=\"" + System.getProperty("java.vm.version") + "\""); } /* * 待Patch的class loader应该是BaseDexClassLoaderd的子类, * MultiDex主要通过修改pathList字段来添加更多的dex */ ClassLoader loader; try { loader = context.getClassLoader(); } catch (RuntimeException e) { /* Ignore those exceptions so that we don't break tests relying on Context like * a android.test.mock.MockContext or a android.content.ContextWrapper with a * null base Context. */ Log.w(TAG, "Failure while trying to obtain Context class loader. " + "Must be running in test mode. Skip patching.", e); return; } if (loader == null) { // Note, the context class loader is null when running Robolectric tests. Log.e(TAG, "Context class loader is null. Must be running in test mode. " + "Skip patching."); return; } // MultiDex的二级dex文件将存放在 /data/data/<package-name>/secondary-dexes 下 File dexDir = new File(context.getFilesDir(), SECONDARY_FOLDER_NAME); // 从apk中查找并解压二级dex文件到/data/data/<package-name>/secondary-dexes List<File> files = MultiDexExtractor.load(context, applicationInfo, dexDir, false); // 检查dex压缩文件的完整性 if (checkValidZipFiles(files)) { // 开始安装dex installSecondaryDexes(loader, dexDir, files); } else { Log.w(TAG, "Files were not valid zip files. Forcing a reload."); // 第一次检查失败,MultiDex会尽责的再检查一次 files = MultiDexExtractor.load(context, applicationInfo, dexDir, true); if (checkValidZipFiles(files)) { // 开始安装dex 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"); }
/*package*/ final class DexPathList { .... /** * Makes an array of dex/resource path elements, one per element of * the given array. */ private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory) { ArrayList<Element> elements = new ArrayList<Element>(); /* * Open all files and load the (direct or contained) dex files * up front. */ for (File file : files) { File zip = null; DexFile dex = null; String name = file.getName(); if (name.endsWith(DEX_SUFFIX)) { // Raw dex file (not inside a zip/jar). try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException ex) { System.logE("Unable to load dex file: " + file, ex); } } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX) || name.endsWith(ZIP_SUFFIX)) { zip = file; try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException ignored) { /* * IOException might get thrown "legitimately" by * the DexFile constructor if the zip file turns * out to be resource-only (that is, no * classes.dex file in it). Safe to just ignore * the exception here, and let dex == null. */ } } else { System.logW("Unknown file type for: " + file); } if ((zip != null) || (dex != null)) { elements.add(new Element(file, zip, dex)); } } return elements.toArray(new Element[elements.size()]); } ... }