如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456    (来自时之沙的csdn博客)

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

  1. UNEXPECTED TOP-LEVEL EXCEPTION:
  2. java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536
  3. at com.android.dx.merge.DexMerger$6.updateIndex(DexMerger.java:501)
  4. at com.android.dx.merge.DexMerger$IdMerger.mergeSorted(DexMerger.java:282)
  5. at com.android.dx.merge.DexMerger.mergeMethodIds(DexMerger.java:490)
  6. at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:167)
  7. at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
  8. at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
  9. at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
  10. at com.android.dx.command.dexer.Main.run(Main.java:230)
  11. at com.android.dx.command.dexer.Main.main(Main.java:199)
  12. 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来索引文件中的方法,也就是最多表达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进行修改:

  1. afterEvaluate {
  2. tasks.matching {
  3. it.name.startsWith('dex')
  4. }.each { dx ->
  5. if (dx.additionalParameters == null) {
  6. dx.additionalParameters = []
  7. }
  8. dx.additionalParameters += '--multi-dex' // enable multidex
  9. // optional
  10. // dx.additionalParameters += "--main-dex-list=$projectDir/<filename>".toString() // enable the main-dex-list
  11. }
  12. }

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

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

  1. android/support/multidex/BuildConfig.class
  2. android/support/multidex/MultiDex$V14.class
  3. android/support/multidex/MultiDex$V19.class
  4. android/support/multidex/MultiDex$V4.class
  5. android/support/multidex/MultiDex.class
  6. android/support/multidex/MultiDexApplication.class
  7. android/support/multidex/MultiDexExtractor$1.class
  8. android/support/multidex/MultiDexExtractor.class
  9. android/support/multidex/ZipUtil$CentralDirectory.class
  10. android/support/multidex/ZipUtil.class

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

  1. android {
  2. defaultConfig {
  3. // Enabling multidex support.
  4. multiDexEnabled true
  5. }
  6. }
  7. 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()方法:

  1. public class MyApplication extends FooApplication {
  2. @Override
  3. protected void attachBaseContext(Context base) {
  4. super.attachBaseContext(base);
  5. MultiDex.install(this);
  6. }
  7. }

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

    1. android {
    2. compileSdkVersion 21
    3. buildToolsVersion "21.1.0"
    4. defaultConfig {
    5. ...
    6. minSdkVersion 14
    7. targetSdkVersion 21
    8. ...
    9. // Enabling multidex support.
    10. multiDexEnabled true
    11. }
    12. ...
    13. }
    14. dependencies {
    15. compile 'com.android.support:multidex:1.0.0'
    16. }

Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚(转)的更多相关文章

  1. Android 使用android-support-multidex解决Dex超出方法数的限制问题

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

  2. android MultiDex multidex原理下超出方法数的限制问题(三)

    android MultiDex 原理下超出方法数的限制问题(三)    插件化?自动化?multiDex?是不是觉得已经懵逼了?请先看这篇文章的内容,在下篇文章中将会详解具体的过程- 随着应用不断迭 ...

  3. Dex文件方法数超过65536怎么破?

    你的应用中的Dex 文件方法数超过了最大值65536的上限,会提示你: UNEXPECTED TOP-LEVEL EXCEPTION:java.lang.IllegalArgumentExceptio ...

  4. Android 65535 问题与 MultiDex分包

    Android Multidex 遇到的问题 http://blog.csdn.net/wangbaochu/article/details/51178881 Android 使用android-su ...

  5. Android 65536方法数限制的思考

    前言 没想到,65536真的很小. 1 Unable to execute dex: method ID not in [0, 0xffff]: 65536 PS:本文只是纯探索一下这个65K的来源, ...

  6. 【转】Android studio 解决64K超出链接数限制问题

    http://my.oschina.net/gabriel1215/blog/602608 目录[-] 使用MultiDex支持库 注意事项 结论 如果你是一个android开发者,你至少听说过的Da ...

  7. Android 65K问题之Multidex原理分析及NoClassDefFoundError的解决方法

    Android 65K问题相信困惑了不少人,尽管AS的出来能够通过分dex高速解决65K问题,可是同一时候也easy由于某些代码没有打包到MainDex里引起NoClassDefFoundError. ...

  8. Android方法数超出限定的问题(multiDex,jumboMode)

    在Android项目开发中,项目代码量过大或通过引入很多jar导致代码量急剧增加,会出现错误: android.dex.DexIndexOverflowException: Cannot merge ...

  9. Android 解决方法数 65536 (65k) 限制

    可能出现的错误信息: Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: ...

随机推荐

  1. 写一个自定义类加载器demo

    public class MyTest16 extends ClassLoader { private String classLoaderName; private String fileExten ...

  2. JDK 9 发布仅数月,为何在生产环境中却频遭嫌弃?

    千呼万唤始出来,在经历了整整一年的跳票之后,Java 9 终于在 9 月 21 日拨开云雾,露出真正的面目.对众多 Java 程序员来说,这一天无疑是一个重大的日子,首先 Java 开发者们再也不用羡 ...

  3. element-uI隐藏表格头部

    1.表格结构定义 :show-header="hiddenTableHeader" 2. data里面定义 hiddenTableHeader:false,

  4. haproxy笔记

    haproxy安装.启动.日志配置 方法1:#安装 yum install haproxy -y #日志配置 sed -i 's/^#$ModLoad imudp/$ModLoad imudp/g' ...

  5. cent OS 7 下安装 python 3.6

    step1:安装依赖环境 # yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readli ...

  6. ANSI 标准C 还定义了如下几个宏

    ANSI 标准C 还定义了如下几个宏:_LINE_ 表示正在编译的文件的行号_FILE_ 表示正在编译的文件的名字预处理名称意义#define 宏定义#undef 撤销已定义过的宏名#include ...

  7. sparql学习sparql示例、dbpedia在线验证

    sparql学习sparql示例.dbpedia在线验证 导言 作为sparql群的群主,自己也不太懂sparql,竟然意外接到了一份作业.好久没有接活了.主要就是复习了一下各个语言怎么写.记录一下, ...

  8. 如何将当前平台升级到SonarQube7.9?[最新]

    整体思路 准备测试数据(实际环境可跳过此步骤) 数据库迁移(从版本7.9开始,SonarQube将不再支持MySQL,Mysql-->PG) Sonar版本升级(6.7.7 -> 7.9. ...

  9. selenium自动化之验证,判断总结(持续更新)

    API命令 1,is_displayed() 例子:driver.find_element_by_css_selector("#id").is_displayed() 该元素是否可 ...

  10. 用 Flask 来写个轻博客 (15) — M(V)C_实现博文页面评论表单

    目录 目录 前文列表 实现 post 视图函数 在 posthtml 中添加表单 效果 前文列表 用 Flask 来写个轻博客 (1) - 创建项目 用 Flask 来写个轻博客 (2) - Hell ...