概述
64k方法数(有的人也称之为65k方法数限制,65k的由来是65536/1000的来),其表现是在构建APP的时候出现编译错误,导致构建失败出现如下错误:
Too many field references:143859843; max is 65536
You may try using --multi-dex option.
其本质是由于可执行文件.dex中的Java方法数超过了65536.
产生的原因
上面已有所提及,Android APK本质上是一个可执行的压缩文件,它里面包含了可执行的classes.dex文件,这个文件是Dalvik字节码文件,这里面存放了程序编译后的所有java代码(包含Framework层,三方库和app自己的函数库)。然而Dalvik可执行文件规范中限制了单个.dex文件最多能引用的方法数是65536个,超出这个范围就会报错。
解决办法
当你的app中的方法数超过64k时,可以引入MultiDex Support Library 来将一个APK中的单一.dex文件拆分成多个,从而规避了64k方法数限制引起的编译错误。但并不建议这样做,原因后边说。
在高版本的Android Build Tool中可直接配置:
defaultConfig {
multiDexEnabled true
}
在低版本中需要手动引入
api ‘com.android.support:multidex:1.0.1’
MultiDex Support Library的局限性
MultiDex Support Library并不是一个完美的解决方案,它集成到项目中需要经过完整的测试才能上线,它的引入可能导致应用的性能下降等问题。
- 耗时:应用首次启动时,Dalvik虚拟机会对所有的.dex文件执行dexopt操作,生成ODEX文件,而这个过程是相当复杂的,非常的耗时,若果从dex文件过大,还有肯能导致ANR的出项。
- 由于Dalvik的先行内存分配器LinearAlloc的限制,使用MultiDex的应用再出现很大的内存分配时,可能导致应用崩溃。根本原因时Dalvik虚拟机用来加载类的堆内存被硬编码了。Android 5.0使用的是ART虚拟机,从而不存在这个问题。
- 引用MultiDex机制,必然就会存在主dex文件和从dex文件,应用启动时所需要的类都要放进主dex,否则会出现NoClassDefFoundError的错误(找不到需要加载的类),这些系统启动必要的类构建工具会自动为我们处理,但是当使用三方库时并且三方库中的实现是通过反射或者调用NDK层代码的Java方法,这些就可能不会被放到dex文件中(因为没有直接的引用关系),这是就会出现问题。
问题深入思考
Android5.0之前,使用的是Dalvik虚拟机,而默认情况下每个APK都只会生成一个dex文件,当项目大一点时方法数不可避免地就会超过64k的限制,为了规避64k的限制,Google推出了MultiDex Support Library的函数库,其原理就是将一个dex文件拆分为多个dex文件,而这些dex文件只有一个主dex文件其他的都是从dex文件,在程序启动时会先去加载主dex文件,程序启动后才会去加载其他的从dex文件。
在Android5.0之后包含5.0,Android使用ART虚拟机代替原先的Dalvik虚拟机。而ART虚拟机天然的支持多个dex文件,在应用安装期间,它会有一个预编译操作,逐个的扫描APK中的众多dex文件,并将它们编译成一个单一的oat文件,在应用运行时加载这个oat文件,而不是原先的dex文件。
优化
代码上
由于64k的限制问题我们在选用三方库时要慎重,需要综合考虑库的体积,方法数,性能等,可能我们引入一个很大的库,但只用了这个库的一小部分功能。出现这种情况我们最好把需要的功能从这个库中抠出来后者找一个功能单一的替代库。
而是使用Proguard移除无用的代码,从而减少方法数,它能检测并移除程序中没有使用到的类,字段,方法和属性。
构建上
当我们在一个较大的项目中引入MultiDex时,这时我们点击构建按钮时可能这时的构建时间比原先将要延长好多,大大影响了我们的开发效率。MultiDex之所以会增加如此显著的构建时间是因为,在构建中要去分析哪些内容需要放入主dex文件中,从而保证程序的正常启动,这是一个很耗时的操作,尤其是程序规模比较大时尤为明显。
为了解决上述问题,可以引入productFlavors,我们可以在build.gradle文件中使用productFlavors来创建两个flavor:一个是开发阶段中使用,一个是上线阶段使用。在开发阶段中的flavor中设置minSdkVersion为21,这样我们的构建系统就回使用ART支持的格式来生成MultiDex文件;再上线阶段的flavor中使用项目最低兼容的最低版本号。
productFlavors {
dev {
minSdkVersion 21
dimension "min"
}
prod {
minSdkVersion 17
dimension "min"
}
}
同步后我们就可以看到
当我们开发时就可以选中devDebug,这时再构建程序就比较快了,步骤:
- Android studio会将项目中的每个module构建成单独的dex文件
- 再将这些dex文件打包到APK中,中间不需要修改
- 需要强调的是在打包到APK的过程中,每个dex文件是不需要合并的,这就省去启动过程中需要的必要文件的计算,大大节省了构建时间。
此外这样配置的另一个好处是:在开发阶段,只有发生了改变的module的dex文件会重新计算生成,而其他没有改变的module的dex文件会维持不变,从而实现了快速的增量构建(需要注意的是以这样的方式生成的APK只能在Android5.0及以上的设备上运行)。