随着安卓平台的不断发展与壮大,市场上大而全的应用比比皆是,产品需求的变更累积和UI交互的极致追求,除了 resources 文件的俱增,在 Android Project 中依赖的 Library 和 自己写的 Java 代码也会越来越多。这些变化,除了会导致打包出的 APK 文件越来越大之外,当项目中java代码包含的方法数(method count)超出一个峰值时。尽管在不同版本的编译系统中显示的错误内容不尽相同,但都提到了一个具体的数字:65536,也就是本文要讲到的核心内容,Android 64K Method Counts Limit 的峰值。
Android 64K Method Counts Limit
Android Project 经过编译打包,其中的Java代码(包括Library)转化为DEX格式的字节码文件,这是Android 5.0之前的Dalvik虚拟机决定的(5.0之后改为ART虚拟机),并且采用short类型引用DEX文件中的method,这也为method数量的峰值大小埋下了隐患。short类型能够表示的最大值是65536,也就说单个DEX文件中最多只有65536个method能够得到引用,如果代码执行了超出部分的method引用,自然会报错,如methodNotFound等。1K等于1024,65536刚好是64K,为了便于称呼和使用,就将这个限制统称为64K方法数的引用限制。
为了解决64K方法数限制的问题,我们可以在项目中使用multidex配置,当项目中的方法数(包括:Android framework,library和我们自己写的代码)超过64K时,编译系统会自动编译出多个DEX文件。
Multidex Support
Android 5.0之前,安卓系统采用的是Dalvik虚拟机,采用的是JIT技术(Just-in-time compilation,即时编译,运行时编译DEX字节码文件,这也是以前为什么安卓手机用户总是诟病Android系统比iOS系统运行卡顿的原因),限制每个APK文件只能包含一个DEX文件(即classes.dex)。为了绕开这个限制,Google给我们提供了multidex support library兼容包,帮助我们实现应用程序加载多个DEX文件,并且这个兼容包作为程序的主DEX文件,管理者其他DEX文件的访问。
Android 5.0之后,安卓系统改用了ART虚拟机(Android RunTime),采用的是OAT技术(Ahead-of-time,预编译,在应用安装的时候扫描应用中的所有DEX文件,并编译成一个.oat格式的文件供安卓设备执行,所以相比Dalvik虚拟机下的应用,安装时间较长)。因此可以理解为,使用ART虚拟机下的安卓系统自动支持APK文件中多个DEX的加载。(注意:使用Instant Run时,如果项目中的minSdkVersion参数设为21或更高版本,Android Studio编译运行时会自动使应用支持multidex。但Instant Run仅仅作用于debug版本,我们依然需要给release版本配置multidex来避开64K方法数的限制。)
Config for Multidex With Gradle
Android Gradle 插件在 Android SDK Build Tools 21.1 及更高版本的编译工具上支持multidex作为编译配置的一部分,所以确保我们的Android SDK Build Tools tools已经更新至21.1或更高版本,然后再来配置应用的multidex部分。
第一步,修改app/build.grale文件,使项目能够使用multidex:
第二步,修改AndroidManifest.xml文件,引用MultiDexApplication类,或者一般我们都是自定义继承Application,此时就需要重写attachBaseContext()方法,并在该方法里面调用MultiDex.install(this)来支持multidex。