Android dex file cannot exceed 64K错误,使用Mutidex解决Dex超出方法数的限制
Posted 台风中的橘子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android dex file cannot exceed 64K错误,使用Mutidex解决Dex超出方法数的限制相关的知识,希望对你有一定的参考价值。
如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456 (来自时之沙的csdn博客)
随着应用不断迭代,业务线的扩展,应用越来越大(比如集成了各种第三方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文件。这里需要强调的是,Dex和Jar一样是一个归档文件,里面仍然是Java代码对应的字节码文件。当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载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插件,其他基本上相同):
[java] view plain copy
- 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的支持,一切都会变得非常简单,首先我们看一下相关源码的目录,具体的原理分析我会在之后的文章中进行讲解:
[java] view plain copy
- 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 中
[java] view plain copy
- 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()方法:
[java] view plain copy
- public class MyApplication extends FooApplication
- @Override
- protected void attachBaseContext(Context base)
- super.attachBaseContext(base);
- MultiDex.install(this);
最后给出build.gradle中的完整配置:
[java] view plain copy
- 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()
- Android开发 Error:The number of method references in a .dex file cannot exceed 64K.
Android Bug:Error:com.android.dex.DexException: Multiple dex files define Landroid/support/design/wi
Error:Error converting bytecode to dex: Cause: com.android.dex.DexException: Multiple dex files defi
Error:Error converting bytecode to dex:Cause: com.android.dex.DexException: Multiple dex files defin
Error:Error converting bytecode to dex:Cause: com.android.dex.DexException: Multiple dex files defin
Android 逆向ART 脱壳 ( 修改 /art/runtime/dex_file.cc#OpenCommon 系统源码进行脱壳 )