Multidex详解

来源:互联网 发布:b超三个数据看男女技巧 编辑:程序博客网 时间:2024/06/01 10:14

Multidex详解

1.使用

1.在项目gralde配置中增加依赖 compile "com.android.support:multidex:1.0.2"2.在AndroidManifest.xml中声明 application为MultiDexApplication,  或者在自定义的application中,重写如下方法    protected void attachBaseContext(Context base) {        super.attachBaseContext(base);        android.support.multidex.MultiDex.install(this);    }

2.源码详解

   public static void install(Context context) {        Log.i("MultiDex", "Installing application");        if(IS_VM_MULTIDEX_CAPABLE) {// vm支持的不需要使用multidex库            Log.i("MultiDex", "VM has multidex support, MultiDex support library is disabled.");        } else if(VERSION.SDK_INT < 4) {// 太早Android版本不支持的            throw new RuntimeException("MultiDex installation failed. SDK " + VERSION.SDK_INT + " is unsupported. Min SDK version is " + 4 + ".");        } else {            try {                //获取安装的apk的信息                ApplicationInfo applicationInfo = getApplicationInfo(context);                if(applicationInfo == null) {                    Log.i("MultiDex", "No ApplicationInfo available, i.e. running on a test Context: MultiDex support library is disabled.");                    return;                }                // 开始执行加载其他dex, 加载到data/data/包名/code_cache/secondary-dexes/                doInstallation(context, new File(applicationInfo.sourceDir), new File(applicationInfo.dataDir), "secondary-dexes", "");            } catch (Exception var2) {                Log.e("MultiDex", "MultiDex installation failure", var2);                throw new RuntimeException("MultiDex installation failed (" + var2.getMessage() + ").");            }            Log.i("MultiDex", "install done");        }    }    private static void doInstallation(Context mainContext, File sourceApk, File dataDir, String secondaryFolderName, String prefsKeyPrefix) throws                 IOException, IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {        Set var5 = installedApk;// 已经加载不再重新加载        synchronized(installedApk) {            if(!installedApk.contains(sourceApk)) {                installedApk.add(sourceApk);               ....                ClassLoader loader;                try {                    loader = mainContext.getClassLoader();                } catch (RuntimeException var11) {                    Log.w("MultiDex", "Failure while trying to obtain Context class loader. Must be running in test mode. Skip patching.", var11);                    return;                }                if(loader == null) {                    Log.e("MultiDex", "Context class loader is null. Must be running in test mode. Skip patching.");                } else {                    try {                        clearOldDexDir(mainContext);                    } catch (Throwable var10) {                        Log.w("MultiDex", "Something went wrong when trying to clear old MultiDex extraction, continuing without cleaning.", var10);                    }                    // 获取存放目录                    File dexDir = getDexDir(mainContext, dataDir, secondaryFolderName);                    // 加载dex                    List<? extends File> files = MultiDexExtractor.load(mainContext, sourceApk, dexDir, prefsKeyPrefix, false);                    // 合并使用dex                    installSecondaryDexes(loader, dexDir, files);                }            }        }    }

3.加载过程

    static List<? extends File> load(Context context, File sourceApk, File dexDir, String prefsKeyPrefix, boolean forceReload) throws IOException {        ...            // 是否强制重新加载或dex文件被修改了, 以apk的最后修改时间和zip的crc来校验apk是否被修改             if(!forceReload && !isModified(context, sourceApk, currentCrc, prefsKeyPrefix)) {                try {                    files = loadExistingExtractions(context, sourceApk, dexDir, prefsKeyPrefix);                } catch (IOException var21) {                    Log.w("MultiDex", "Failed to reload existing extracted secondary dex files, falling back to fresh extraction", var21);                    files = performExtractions(sourceApk, dexDir);                    putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), currentCrc, files);                }            } else {                Log.i("MultiDex", "Detected that extraction must be performed.");                // 准备dex                files = performExtractions(sourceApk, dexDir);                // 保存dex的信息(最后时间和crc信息)到multidex.version的sp配置中                putStoredApkInfo(context, prefsKeyPrefix, getTimeStamp(sourceApk), currentCrc, files);            }        ...    }    private static List<ExtractedDex> performExtractions(File sourceApk, File dexDir) throws IOException {            ....           int secondaryNumber = 2;            // 从apk中找出dex文件            for(ZipEntry dexFile = apk.getEntry("classes" + secondaryNumber + ".dex"); dexFile != null; dexFile = apk.getEntry("classes" +secondaryNumber + ".dex")) {                String fileName = extractedFilePrefix + secondaryNumber + ".zip";// 创建dex对应的zip文件                ExtractedDex extractedFile = new ExtractedDex(dexDir, fileName);                files.add(extractedFile);                Log.i("MultiDex", "Extraction is needed for file " + extractedFile);                int numAttempts = 0;                boolean isExtractionSuccessful = false;                while(numAttempts < 3 && !isExtractionSuccessful) {                    ++numAttempts;                    // 把dex文件提取出来放到对应的zip文件中, 重试3次                    extract(apk, dexFile, extractedFile, extractedFilePrefix);                    try {                        extractedFile.crc = getZipCrc(extractedFile);                        isExtractionSuccessful = true;                    } catch (IOException var19) {                        isExtractionSuccessful = false;                        Log.w("MultiDex", "Failed to read crc from " + extractedFile.getAbsolutePath(), var19);                    }                    Log.i("MultiDex", "Extraction " + (isExtractionSuccessful?"succeeded":"failed") + " - length " + extractedFile.getAbsolutePath() + ": " + extractedFile.length() + " - crc: " + extractedFile.crc);                    if(!isExtractionSuccessful) {                        extractedFile.delete();                        if(extractedFile.exists()) {                            Log.w("MultiDex", "Failed to delete corrupted secondary dex '" + extractedFile.getPath() + "'");                        }                    }                }                if(!isExtractionSuccessful) {                    throw new IOException("Could not create zip file " + extractedFile.getAbsolutePath() + " for secondary dex (" + secondaryNumber + ")");                }                ++secondaryNumber;            }        ...    }    private static void putStoredApkInfo(Context context, String keyPrefix, long timeStamp, long crc, List<ExtractedDex> extractedDexes) {        SharedPreferences prefs = getMultiDexPreferences(context);        Editor edit = prefs.edit();        edit.putLong(keyPrefix + "timestamp", timeStamp);        edit.putLong(keyPrefix + "crc", crc);        edit.putInt(keyPrefix + "dex.number", extractedDexes.size() + 1);        int extractedDexId = 2;        for(Iterator var10 = extractedDexes.iterator(); var10.hasNext(); ++extractedDexId) {            ExtractedDex dex = (ExtractedDex)var10.next();            edit.putLong(keyPrefix + "dex.crc." + extractedDexId, dex.crc);            edit.putLong(keyPrefix + "dex.time." + extractedDexId, dex.lastModified());        }        edit.commit();    }

4.合并dex

    private static void installSecondaryDexes(ClassLoader loader, File dexDir, List<? extends File> files) throws IllegalArgumentException,     IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException {        if(!files.isEmpty()) {            if(VERSION.SDK_INT >= 19) {                V19.install(loader, files, dexDir);            } else if(VERSION.SDK_INT >= 14) {                V14.install(loader, files, dexDir);            } else {                V4.install(loader, files);            }        }    }    这里先说明下原理:    //dalvik.system.DexPathList android遍历dexElements的来加载类的, 所以只要把dex的dexElements合并到apk的pathClassLoade的dexElements中就可以加载到dex中的类    public Class findClass(String name, List<Throwable> suppressed) {        for (Element element : dexElements) {            DexFile dex = element.dexFile;            if (dex != null) {                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);                if (clazz != null) {                    return clazz;                }            }        }        if (dexElementsSuppressedExceptions != null) {            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));        }        return null;    }    private static final class V19 {        private V19() {        }        private static void install(ClassLoader loader, List<? extends File> additionalClassPathEntries, File optimizedDirectory) throws    IllegalArgumentException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {                Field pathListField = MultiDex.findField(loader, "pathList");                Object dexPathList = pathListField.get(loader);                ArrayList<IOException> suppressedExceptions = new ArrayList();                合并dex的dexElements到pathClassLoade的dexElements中, 通过反射实现                MultiDex.expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new ArrayList(additionalClassPathEntries), optimizedDirectory, suppressedExceptions));            ...        }        // 构建dex的DexElements, 在Android6.0之后的DexPathList没有makeDexElements这个方法, 这就是前面判断vm自身支持multidex,不需要再使用multidex库了        private static Object[] makeDexElements(Object dexPathList, ArrayList<File> files, File optimizedDirectory, ArrayList<IOException>  suppressedExceptions) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {                Method makeDexElements = MultiDex.findMethod(dexPathList, "makeDexElements", new Class[]{ArrayList.class, File.class, ArrayList.class});                return (Object[])((Object[])makeDexElements.invoke(dexPathList, new Object[]{files, optimizedDirectory, suppressedExceptions}));        }    }
原创粉丝点击