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);// 加载dexList<? extends File> files = MultiDexExtractor.load(mainContext, sourceApk, dexDir, prefsKeyPrefix, false);// 合并使用dexinstallSecondaryDexes(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.");// 准备dexfiles = 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}));}}

Multidex详解相关推荐

  1. android仿知乎按钮动效,Android仿知乎客户端关注和取消关注的按钮点击特效实现思路详解...

    先说明一下,项目代码已上传至github,不想看长篇大论的也可以先去下代码,对照代码,哪里不懂点哪里. 代码在这https://github.com/zgzczzw/ZHFollowButton 前几 ...

  2. build.gradle配置参数详解

    转载地址:http://blog.csdn.net/baidu_31093133/article/details/51860637 build.gradle配置参数详解//声明是Android程序ap ...

  3. GreenDao 使用详解(入门篇)

    Android ORM 框架:GreenDao 使用详解 最近太忙了,直接引用网上的图片 一.GreenDao 简介 greenDAO 是一款开源的面向 Android 的轻便.快捷的 ORM 框架, ...

  4. ExoPlayer详解(官方文档-入门)

    目录 ExoPlayer详解系列文章-入门 一.前言 二.优缺点比较 三.概述 ExoPlayer详解--入门(官方文档) 添加ExoPlayer作为依赖项 1.添加依赖 2.添加ExoPlayer模 ...

  5. Android 8.0学习(32)---Android 8.0源码目录结构详解

    Android 8.0源码目录结构详解 android的移植按如下流程:     (1)android linux 内核的普通驱动移植,让内核可以在目标平台上运行起来.     (2)正确挂载文件系统 ...

  6. oracle spool文件名+系统时间,Oracle Spool详解

    转自:http://blog.sina.com.cn/s/blog_6bccf0360101hzsh.html 1.spool的作用是什么? spool的作用可以用一句话来描述:在sqlplus中用来 ...

  7. support库中v4 v7版本冲突错误详解

    support中v4 v7库版本错误,主要会报以下两种错误,下面针对每一种错误进行详解 第一种 错误信息: 先来看两个崩溃信息,如下: java.lang.NoClassDefFoundError: ...

  8. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  9. JVM年轻代,老年代,永久代详解​​​​​​​

    秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...

最新文章

  1. 树莓派电压过低 串口数据错误增多
  2. [译]LINT TO SQL 介绍(数据库查询) - Part.3
  3. Handler(上)——Mars Andoird开发视频第二季第六集(重)
  4. 【软件工程】填空题题库
  5. 反射:InvokeMethod 活动调用多种方法的方法配置要点
  6. linux ruby 安装路径,Linux(CentOS 7)安装ruby
  7. pytorch保存和加载文件的方法,从断点处继续训练
  8. 如何创建和自定义SQL Server模板
  9. MNIST数据集的下载
  10. 算法导论第三版 第4章习题答案
  11. 22二叉树非递归遍历算法
  12. webstorm自定义文件模板
  13. LoRa vs LTE-M vs Sigfox
  14. 计算机三级网络技术(选择高频考点)
  15. 赞美CSDN 我去年买了个登山包超耐磨。
  16. 计算一个球的体积的程序HTML,【编程1小时】球体表面积和体积计算
  17. 小米游戏本 linux 编程键,小米笔记本安装Ubuntu后特殊键终于可用了
  18. 树莓派GPIO入门(一):点亮一个LED灯
  19. LAN: Lightweight Attention-based Network for RAW-to-RGB Smartphone Image Processing LAN:用于 RAW 到 RGB
  20. SQL转换日期格式dd-mm-yyyy为yyyy-mm-dd

热门文章

  1. 直播or短视频,谁才是大势所趋?
  2. 【报告分享】2020-2021生活美容行业报告-美业颜究院CIBE中国国际美博会(附下载)
  3. uwsgi概念和作用
  4. Python模块 glob
  5. 【独角兽咳血系列 第二篇】
  6. React.js 之筛选篇
  7. 简单运动函数(JS定时器)
  8. python Gooey库打包问题
  9. foxmail企业邮件服务器,如何用foxmail连接企业邮箱?
  10. win32api之修改revit状态栏提示