随着应用不断迭代,业务线的扩展,应用越来越大(比如集成了各种第三方sdk或者公共支持的jar包,项目耦合性高,重复作用的类越来越多),相信很多人都遇到过如下的错误:

UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536
at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501)
at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:282)
at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490)
at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
at com.android.dx.command.dexer.Main.run(Main.java:230)
at com.android.dx.command.dexer.Main.main(Main.java:199)
at com.android.dx.command.Main.main(Main.java:103)

 没错,你的应用中的Dex 文件方法数超过了最大值65536的上限,简单来说,应用爆棚了.

        那么让我们看一下为什么会引起这种错误:

Android系统中,一个App的所有代码都在一个Dex文件里面。Dex是一个类似Jar的存储了多有Java编译字节码的归档文件。因为Android系统使用Dalvik虚拟机,所以需要把使用Java Compiler编译之后的class文件转换成Dalvik能够执行的class文件。这里需要强调的是,DexJar一样是一个归档文件,里面仍然是Java代码对应的字节码文件。当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOptDexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。  但是在早期的Android系统中,DexOpt的LinearAlloc存在着限制: Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当方法数量过多导致超出缓冲区大小时,会造成dexopt崩溃,导致无法安装.

另外由于DEX文件格式限制,一个DEX文件中method个数采用使用原生类型short来索引文件中的方法,也就是4个字节共计最多表达65536个method,field/class的个数也均有此限制。对于DEX文件,则是将工程所需全部class文件合并且压缩到一个DEX文件期间,也就是Android打包的DEX过程中, 单个DEX文件可被引用的方法总数被限制为65536(自己开发以及所引用的Android Framework和第三方类库的代码).

目前比较常用的方法:(1) 应用插件化,比如使用我正在参与开发的插件化框架 : https://github.com/singwhatiwanna/dynamic-load-apk ,如果有建议或者相关的问题,欢迎到Github上积极参与. (2) 分割Dex,多工程: 把所需要的.class文件或者是Jar文件和一些源码一起编译生成一个Jar文件。然后使用Android SDK提供的dx工具把Jar文件转成Dex文件。(可参考facebook:https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920,这里边还可以看到在2.3上动态改变LinearAlloc缓冲的解决思路) 这两种方法并不冲突,插件化除了解决应用爆棚,还有很多其他的优点,可以看我之前的文章,不再复述.

当然,Google看来也意识到了目前应用方法数爆棚的问题, 目前在已经在API 21中提供了通用的解决方案,那就是android-support-multidex.jar. 这个jar包最低可以支持到API 4的版本(Android L及以上版本会默认支持mutidex).

让我们看一下如何应用android-support-multidex.jar(以下都以在Anroid studio中的使用为例,使用eclipse开发需要安装gradle插件,其他基本上相同):

首先可以用--multi-dex配置(build.gradle)进行解决,生成的Apk中将包含多个dex文件,比如classes.dex, classes2.dex. 如下,需要对build.gradle进行修改:

afterEvaluate {  tasks.matching {  it.name.startsWith('dex')  }.each { dx ->  if (dx.additionalParameters == null) {  dx.additionalParameters = []  }  dx.additionalParameters += '--multi-dex' // enable multidex  // optional  // dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString() // enable the main-dex-list  }
}  

但是默认的Dalvik 类加载器只会寻找classes.dex,所以需要将它们进行合并才能使得被识别

当然,现在有了android.support.multidex.jar的支持,一切都会变得非常简单,首先我们看一下相关源码的目录,具体的原理分析我会在之后的文章中进行讲解:

android/support/multidex/BuildConfig.class
android/support/multidex/MultiDex$V14.class
android/support/multidex/MultiDex$V19.class
android/support/multidex/MultiDex$V4.class
android/support/multidex/MultiDex.class
android/support/multidex/MultiDexApplication.class
android/support/multidex/MultiDexExtractor$1.class
android/support/multidex/MultiDexExtractor.class
android/support/multidex/ZipUtil$CentralDirectory.class
android/support/multidex/ZipUtil.class  

具体集成:
将如下配置加入工程  build.gradle 中

android {  defaultConfig {  // Enabling multidex support.  multiDexEnabled true  }
}
dependencies {  compile 'com.google.android:multidex:0.1'}  

MultiDex实现原理:

Apk在运行的时候,有一个dexpathlist,而Multidex的源码中,会根据你的系统版本号对dexpathlist做修改,将所有的dex都添加到dexpathlist中.

接下来集成有两个步骤:

一. 从sdk\extras\android\support\multidex\library\libs 目录将android-support-multidex.jar导入工程中

二. 如果你的工程中已经含有Application类,那么让它继承android.support.multidex.MultiDexApplication类,

     如果你的Application已经继承了其他类并且不想做改动,那么还有另外一种使用方式,覆写attachBaseContext()方法:

public class MyApplication extends FooApplication {  @Override  protected void attachBaseContext(Context base) {  super.attachBaseContext(base);  MultiDex.install(this);  }
}  

最后给出build.gradle中的完整配置:

android {  compileSdkVersion 21  buildToolsVersion "21.1.0"  defaultConfig {  ...  minSdkVersion 14  targetSdkVersion 21  ...  // Enabling multidex support.  multiDexEnabled true  }  ...
}  dependencies {  compile 'com.android.support:multidex:1.0.0'
}  

使用MutiDex的主意事项


一. 如果你继承了MutiDexApplication或者覆写了Application中的attachBaseContext()方法.

Application类中逻辑的注意事项:

Application 中的静态全局变量会比MutiDex instal()方法优先加载,所以建议避免在Application类中使用静态变量引用main classes.dex文件以外dex文件中的类,可以根据如下所示的方式进行修改:

@Override  public void onCreate() {  super.onCreate();  final Context mContext = this;  new Runnable() {  @Override  public void run() {  // put your logic here!  // use the mContext instead of this here  }  }.run();  }  

二. 虽然Google解决了应用总方法数限制的问题,但并不意味着开发者可以任意扩大项目规模。Multidex仍有一些限制:

  1. DEX文件安装到设备的过程非常复杂,如果第二个DEX文件太大,可能导致应用无响应。此时应该使用ProGuard减小DEX文件的大小。
  2. 由于Dalvik linearAlloc的Bug,应用可能无法在Android 4.0之前的版本启动,如果你的应用要支持这些版本就要多执行测试。
  3. 同样因为Dalvik linearAlloc的限制,如果请求大量内存可能导致崩溃。Dalvik linearAlloc是一个固定大小的缓冲区。在应用的安装过程中,系统会运行一个名为dexopt的程序为该应用在当前机型中运行做准备。dexopt使用LinearAlloc来存储应用的方法信息。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当方法数量过多导致超出缓冲区大小时,会造成dexopt崩溃。
  4. Multidex构建工具还不支持指定哪些类必须包含在首个DEX文件中,因此可能会导致某些类库(例如某个类库需要从原生代码访问Java代码)无法使用。

避免应用过大、方法过多仍然是Android开发者要注意的问题。Mihai Parparita的开源项目dex-method-counts可以用于统计APK中每个包的方法数量。

通常开发者自己的代码很难达到这样的方法数量限制,但随着第三方类库的加入,方法数就会迅速膨胀。因此选择合适的类库对Android开发者来说尤为重要。

开发者应该避免使用Google Guava这样的类库,它包含了13000多个方法。尽量使用专为移动应用设计的Lite/Android版本类库,或者使用小类库替换大类库,例如用Google-gson替换Jackson JSON。而对于Google Protocol Buffers这样的数据交换格式,其标准实现会自动生成大量的方法。采用Square Wire的实现则可以很好地解决此问题。

常见问题

DexException: Library dex files are not supported in multi-dex mode ,你可能会见到如下的错误:

Error:Execution failed for task ':app:dexDebug'.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:  $ANDROID_SDK/build-tools/android-4.4W/dx --dex --num-threads=4 --multi-dex  ...  Error Code:  2  Output:  UNEXPECTED TOP-LEVEL EXCEPTION:  com.android.dex.DexException: Library dex files are not supported in multi-dex mode  at com.android.dx.command.dexer.Main.runMultiDex(Main.java:322)  at com.android.dx.command.dexer.Main.run(Main.java:228)  at com.android.dx.command.dexer.Main.main(Main.java:199)  at com.android.dx.command.Main.main(Main.java:103) 

对于dex 的--multi-dex 选项设置与预编译的library工程有冲突,因此如果你的应用中包含引用的lirary工程,需要将预编译设置为false:

android {  // ...  dexOptions {  preDexLibraries = false  }
}  

OutOfMemoryError: Java heap space

当运行时如果看到如下错误:

UNEXPECTED TOP-LEVEL ERROR:
java.lang.OutOfMemoryError: Java heap space  

在dexOptions中有一个字段用来增加java堆内存大小:

android {  // ...  dexOptions {  javaMaxHeapSize "2g"  }
}      

使用eclipse的开发人员可以参考Google官方文档

https://developer.android.com/tools/building/multidex.html#mdex-gradle

需要安装支持gradle构建的插件,下载地址:

http://dist.springsource.com/release/TOOLS/gradle  (目前可能需要翻墙)

参考相关资料:

1. MutiDex 官方文档: https://developer.android.com/reference/android/support/multidex/MultiDex.html

2. http://blog.osom.info/2014/10/multi-dex-to-rescue-from-infamous-65536.html

另附android -support-mutidex.jar下载地址: http://download.csdn.net/detail/t12x3456/8143383

补充注意事项:

com.android.dex.DexException: Multiple dex files define L{package}/BuildConfig;  

如果遇到这个错误请进行如下检查:

1. 主工程与依赖library工程包名是否重复
2. 检查主工程与依赖library工程是否含有重复的support.jar或者其他jar包


解决方案:

1. 修改library工程包名

2. 删除重复jar包

3.手工添加lib包,增加如下配置

dependencies {  compile fileTree(dir: 'libs', include: ['*.jar'])  compile project(':lib-project-module')  }  

MultiDex使用方法相关推荐

  1. Android 开启 multidex的方法

    build后会生成此文件 ======================================================= 开启multidex的方法: 步骤演示: 然后点击build, ...

  2. Android MultiDex实践:如何绕过那些坑?

    本文是我的同事徐冬投稿,介绍他在方法数到达65k上限后,应用官方MultiDex方案时踩过的一些坑,以及如何解决这些棘手问题的实践过程. 前言 Android应用65k方法数的限制一直为广大开发者所诟 ...

  3. MultiDex从基础原理到实践优化

    参考:学习视频 课程内容 1. 背景知识 1.1 Class文件与Dex文件 1.2 方法数超限问题与解决思路 2. 基本用法 2.1 Android 5.0 及更高版本 2.2 Android 5. ...

  4. 一文详解 Android multidex 使用方式及实现原理

    在Android中一个Dex文件最多存储65536个方法,也就是一个short类型的范围.但随着应用方法数量的不断增加,当Dex文件突破65536方法数量时,打包时就会抛出异常. 为解决该问题,And ...

  5. MultiDex分包

    MultiDex分包 摘要 1,简单说下multiDex的由来 2.multiDex的局限 3.multiDex的使用 摘要 由于经常在sdk项目中用到multiDex,对于android官网大段的描 ...

  6. MultiDex工作原理

    看这篇文章前可以先看一下了解一下上篇:ClassLoader的工作原理(Java中和Andriod中的一些区别) 没有引入MultiDex之前,整个app里就一个dex,对于这个dex的加载在安装的时 ...

  7. Android 进阶——MultiDex分包与动态加载原理剖析

    一.为什么要进行Dex分包 Android下单个Dex文件存在65535函数数量的限制,而一个功能稍微复杂点的APP很容易超过这个限制,为此,我们引入了Dex分包,将一个App所有class分别打包为 ...

  8. dex文件结构(三):dex文件差分包分成

    作者:hackest 链接:https://www.jianshu.com/p/5a2e33a61ba2 当程序越来越大之后,出现了一个 dex 包装不下的情况,通过 MultiDex 的方法解决了这 ...

  9. Android-Multidex安装流程解析

    Android-Multidex安装流程解析 关于为什么需要引入Multidex支持以及如何配置Multidex可参考官网,本篇不做阐述,本篇着重分析Multidex1.0.2源码进行分析 大家都知道 ...

最新文章

  1. FPGA的设计艺术(5)STA实战之时钟偏斜对建立保持时间的影响以及时序报告分析
  2. linux部署MantisBT(三)部署MantisBT
  3. win10 uwp 按下等待按钮
  4. java socket 路由_JAVA简单的Socket网络编程!CS
  5. 前端学习(2470):echart复习电商管理通过erchart基本使用
  6. 模型开发:拥抱张量运算
  7. layui-treeTable v2.0添加搜索功能
  8. 教程 参数设置_AI全自动剪辑软件安装教程,参数设置与使用注意事项
  9. 状态转移矩阵 matlab,状态转移矩阵计算.PPT
  10. tas5707php,TAS5707PHPR 立体声数字音频功率放大器
  11. 亲属卡额度是什么意思_2019信用卡权益总结之十二:附属卡
  12. t检验的几种应用案例
  13. IDF实验室之万里寻踪红与黑
  14. 机器学习实战(Machine Learning in Action)学习笔记————07.使用Apriori算法进行关联分析...
  15. 用Python完成 “Excel合并(拆分)” 的各种自动化操作!
  16. java8通讯_8.进程通信
  17. git 新建分支并切换到该分支_Git 从master拉取代码创建新分支 并且再将修改合并到master...
  18. Centos 安装Firefox
  19. 【消息队列】五个问题详解消息中间件
  20. 中国人必须知道的76个常识。。。看了才发现,自己知道的实在太少了

热门文章

  1. Android Snackbar使用详解
  2. 学习笔记:robotframework脚本
  3. 系统集成项目管理工程师 42 个过程图形记忆法(第2期)
  4. 显而易见的事情很容易忽略
  5. R语言绘制Donut chart
  6. 根据ip地址获取地理位置及坐标(离线方式)
  7. c/c++字节序转换(转)
  8. 向上级临时汇报工作的方法
  9. CS versus EE
  10. 基于HTML5的超级玛丽游戏demo