- 一、前言
- 二、产生原因
- 三、MultiDex的简要原理
- 四、MultiDex的使用
一、前言
首先说一下我遇到的情况,最近接手了一个项目是在已有的项目里进行更新添加一些功能,然后该项目导了N多的包,在我使用Android Studio的run”App”直接安装到手机上运行是正常的,然后正式打包安装后就崩溃了,当时觉得很奇怪,然后一看日志:
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
没办法只好查资料:
Note: While using Instant Run, Android Studio automatically configures your app for multidex when your app's minSdkVersion is set to 21 or higher. Because Instant Run only
works with the debug version of your app, you still need to configure your release build for multidex to avoid the 64K limit.
大概意思就是:
如果使用Instant Run,当app的minSdkVersion大于或等于21时,Android Studio会自动配置支持multidex,但是仅debug版本有效,release版仍然需要配置multidex来突破64K限制。
二、产生原因
在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有一个问题,也就是这篇文章想要说明并解决的问题。DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。尽管在新版本的Android系统中,DexOpt修复了这个问题,但是我们仍然需要对低版本的Android系统做兼容。
为了解决方法数超限的问题,需要将该dex文件拆成两个或多个,为此谷歌官方推出了multidex兼容包,配合AndroidStudio实现了一个APK包含多个dex的功能。
三、MultiDex的简要原理
我们以APK中有两个dex文件为例,第二个dex文件为classes2.dex。
兼容包在Applicaion实例化之后,会检查系统版本是否支持 multidex,classes2.dex是否需要安装。
如果需要安装则会从APK中解压出classes2.dex并将其拷贝到应用的沙盒目录下。
通过反射将classes2.dex注入到当前的classloader中。
下面引入一下官方的文档:
https://developer.android.com/tools/building/multidex.html#about
四、MultiDex的使用
1、在Gradle中配置使用Multidex
由于Android的Gradle插件在Android Build Tool 21.1开始支持使用multidex,所以我们需要使用Android Build Tools 21.1及以上版本,修改app目录下的build.gradle文件,有两点需要修改。
(1)在defaultConfig中添加multiDexEnabled true这个配置项。
(2)在dependencies中添加multidex的依赖:
compile ‘com.android.support:multidex:1.0.1’
2、在Gradle中配置好之后,我们还需要在代码中加入支持multidex的功能,有三种方案可选
方案一:在manifest文件中指定Application为MultiDexApplication,如下:
方案二:写一个Application类并继承MultiDexApplication,并在AndroidManifest.xml的application标签中进行注册(在application标签中增加name属性,并添加自己的Application类名即可),如果不是想重写MultiDexApplication中一些方法的话,还是方案一更方便些。如下:
注册如下:
方案三:如果不想按方案二继承,我们可以重写Application的attachBaseContext方法,注意,这个方法比onCreate方法先执行。具体方法是创建一个新类,继承Application,然后重写attachBaseContext方法,并在AndroidManifest.xml的application标签中进行注册(与方案二注册相同)如下:
对于在AndroidManifest.xml中注册,与方案二的注册相同。