安卓解决65535dex文件方法数越界,IllegalArgumentException问题

来源:互联网 发布:网络推广经理职责 编辑:程序博客网 时间:2024/05/19 20:46

在Android中,有一个限制,那就是整个应用的方法数不能超过65536,否则就会出现编译错误,并且程序也无法成功地安装到手机上。当项目日益庞大后这个问题就比较容易遇到,Google提供了multidex方案专门用于解决这个问题,通过将一个dex文件拆分为多个dex文件来避免单个dex文件方法数越界的问题。
方法数越界的另一种解决方案是动态加载。动态加载可以直接加载一个dex形式的文件,将部分代码打包到一个单独的dex文件中(也可以是dex格式的jar或者apk),并在程序运行时根据需要去动态加载dex中的类,这种方式既可以解决缓解方法数越界的问题,也可以为程序提供按需加载的特性,同时这还为应用按模块更新提供了可能性。

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) 

首先可以用–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  

具体集成:
将如下配置加入工程 classPath中

repositories {    jcenter()  }  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仍有一些限制:
DEX文件安装到设备的过程非常复杂,如果第二个DEX文件太大,可能导致应用无响应。此时应该使用ProGuard减小DEX文件的大小。
由于Dalvik linearAlloc的Bug,应用可能无法在Android 4.0之前的版本启动,如果你的应用要支持这些版本就要多执行测试。
同样因为Dalvik linearAlloc的限制,如果请求大量内存可能导致崩溃。Dalvik linearAlloc是一个固定大小的缓冲区。在应用的安装过程中,系统会运行一个名为dexopt的程序为该应用在当前机型中运行做准备。dexopt使用LinearAlloc来存储应用的方法信息。Android 2.2和2.3的缓冲区只有5MB,Android 4.x提高到了8MB或16MB。当方法数量过多导致超出缓冲区大小时,会造成dexopt崩溃。
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"      }  }  

附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')          } 

参考:singwhatiwanna

7 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 鼻综合鼻内增生怎么办 做完鼻综合脸肿怎么办 上海车子卖了etc怎么办 车卖了etc设备怎么办 车卖了etc没摘怎么办 交通信息卡丢了怎么办 多囊卵巢无排卵怎么办 卵巢腹镜手术后一直出汗怎么办 双侧卵巢多囊怎么办 备孕子宫内膜厚怎么办 有成熟卵泡不破怎么办 卵泡不排萎缩了怎么办 卵泡8mm一直不长怎么办 卵泡已经长到28x19mm怎么办 多囊卵巢卵泡长不大怎么办 优势卵泡19不排怎么办 多囊卵泡不排卵怎么办 卵泡两天长2mm怎么办 子宫小43*38*26怎么办 优势卵泡打破卵针后并不破怎么办 ktv禁止自带酒水怎么办 记名西瓜卡丢了怎么办 日本电车卡丢了怎么办 网贷暂时没钱还怎么办 华泰倒闭了汽车怎么办 猫躲起来找不到了怎么办 狗生病了不吃饭怎么办 猫猫托运后害怕怎么办 新来的猫害怕怎么办 升工资老板不公平对待怎么办 自酿啤酒苦味重怎么办 自酿啤酒酸味重怎么办 微信电话费充错了怎么办 支付宝电话费充错了怎么办 在淘宝上充错电话费了怎么办 话费1000充错了怎么办 东西掉在地铁上怎么办 高铁安检丢东西怎么办 东西掉成都地铁上怎么办 东西掉在成都地铁上怎么办 成都地铁上掉东西了怎么办