64K方法数限制原理与解决方案
- 64K限制的原因
- 使用MultiDex解决64K限制的问题
- 如何避免出现64K限制
- 配置MultiDex
- MultiDex Support Library的局限性
- 在开发阶段优化MultiDex的构建
64K限制的原因
Android APK文件本质上是一个压缩文件,它里面包含的classes.dex文件是可执行的Dalvik字节码文件,这个.dex文件中存放的是所有编译后的Java代码。Dalvik可执行文件规范(实际上是最初设计上的一个失误)限制了单个.dex文件最多能引用的方法数是65536个,这其中包含了Android Framework、APP引用的第三方函数库、APP自身的方法。
使用MultiDex解决64K限制的问题
Android5.0之前的版本
在Android5.0(API 21)之前,系统使用的是Dalvik虚拟机来执行Android应用,默认情况下,Dalvik为每个APK只生成一个classes.dex文件,为了规避单个.dex文件方法数超过64K的问题,我们需要拆分这个单一的classes.dex文件,拆分后可能存在类似于classes.dex、classes2.dex、classes3.dex等多个文件,具体有多少个文件要看我们开发中使用的方法总数。在应用启动时,会先加载classes.dex文件,我们称之为主dex文件,应用启动后才会依次加载其他.dex文件,这些统称为从dex文件。为了规避64K方法数限制,Google推出了一个名为MultiDex Support Library的函数库.
Android5.0及之后的版本
从Android5.0开始,Android使用名为ART的虚拟机来代替Dalvik虚拟机,ART天然支持从APK文件中加载多个.dex文件,在应用安装期间,它会执行一个预编译操作,扫描APK中的classes.dex及classes2.dex等文件并将它们编译成一个单一的.oat文件,在应用运行时去加载这个.oat文件,而不是一个一个地加载.dex文件。
如何避免出现64K限制
方法如下:
1 . 检查应用的直接和间接使用的第三方依赖,对于一些冗余的,就把这些剔除掉;
2 . 使用ProGuard移除无用的代码。在Release版本中使用ProGuard,它的压缩功能通过分析字节码,能够检测并移除没有使用的类、字段、方法和属性。
配置MultiDex
当方法数超过64K以后,就使用MultiDex解决方案。Android的Gradle插件在Android Build Tool 21.1开始就支持使用MultiDex了。首先需要配置Application Module的build.gradle文件,在其中增加对MultiDex函数库的依赖。
android{
defaultConfig{
//支持multidex;
multiDexEnabled true
}
}
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
接着引入MultiDexApplication,需要根据项目的具体情况来决定如何引入。
1 . 如果项目中没有实现自定义的Application类,那么只需要在Application Module的AndroidManifest.xml文件中使用MultiDexApplication替换Application就可以了;
<application
android:name="android.support.multidex.MultiDexApplication"
>
</application>
2 . 如果已经有自定义的Application类,那么让它继承自MultiDexApplication类就可以了;
public class MyApplication extends MultiDexApplication{
@Override
public void onCreate(){
super.onCreate();
}
}
3 . 如果已经有了自定义的Application类,而且不修改它的父类,也就是不让它继承自MultiDexApplication类,那么通过覆写attachBaseContext()方法并初始化MultiDex就可以了;attachBaseContext()方法在onCreate()方法之前执行。
public class MyApplication extends Application{
@Override
protected void attachBaseContext(Context base){
super.attachBaseContext(base);
MultiDex.install(this);
}
}
MultiDex Support Library的局限性
使用MultiDex Support Library库只是一个不得已而为之的解决方案,将这个库集成到项目中,可能会出现应用性能下降等问题。
1 . 应用首次启动时,Dalvik虚拟机会对所有的.dex文件执行dexopt操作,生成ODEX文件,这个过程很复杂且非常耗时,如果应用的从dex文件太大,可能会导致出现ANR;
2 . 在Android4.0(API 14)之前的系统上,由于Dalvik linearAlloc的bug,使用MultiDex的应用可能启动失败;
3 . 由于Dalvik的线性内存分配器的linearAlloc的限制,使用MultiDex的应用在出现很大的内存分配时,可能会导致应用崩溃。当然,在Android 5.0 开始使用ART虚拟机,这个问题就不存在了;
4 . 当引入MultiDex机制时,必然会存在主dex文件和从dex文件,应用启动所需要的类都必须放到主dex文件中,否则会出现NoClassDefFoundError的错误。Android构建工具自动帮我们处理了Android系统相关的依赖,但对于应用自己引入的第三方函数库,如果还依赖其他的一些东西,例如通过反射调用Java类,或者调用NDK层代码的Java方法,这些可能就不会被放到主dex文件中,如果在应用启动时需要用到,那么必然出现问题;
在开发阶段优化MultiDex的构建
使用MultiDex以后,第一次编译会比较慢,因为构建系统需要经过计算来决定哪些类要包含在主dex文件中,哪些类可以包含在从dex文件中。
为了减少开发阶段由于引入MultiDex所增加的构建时间,我们可以在工程主模块的build.gradle文件中使用productFlavors来创建两个flavor:一个是开发阶段使用的,另一个是生产阶段使用的。
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
--------------------last line for now-------------------