Android在5.0以前一个APK的方法数大小不能超过65536,否则就无法被编译。这个问题一直在Android 5.0的推出前都没有一个官方的解决方案,随着5.0的推出,Android也放出了Multidex Support Library来解决这个问题。

这里不讨论如何使用Multidex,官方文档里面写得非常清楚,主要来说下Multidex的实现方法,它是怎么样将class编译进不同的dex文件中的。

Multidex的实现原理

Multidex的实现原理是将class编译进不同的classes.dex文件中,一般情况下,一个APK文件中只包含了一个classes.dex文件。分包之后就存在一个主的classes.dex,多个副的classes2.dex,classes3.dex…

在要启动程序时,Android会先去加载主的classes.dex,然后在程序启动后再去加载其它副的dex。那哪些class应该被编译到主的classes.dex中呢?

先来看下Multidex的编译过程,它由三个不同的gradle task组成(不知道什么是Gradle Task的可以去看下我前面写的博客Gradle的基本使用(二)):

1
collect{variant}MultiDexComponents task

这个task会读取项目的AndroidManifest.xml文件中注册的application、Activity、service、receiver、provider、instrumentation相关类,并将其class文件路径写到文件buidl/intermediates/multi-dex/${variant.dirName}/manifest_keep.txt

1
shrink{variant}MultiDexComponents task

这个task会调用ProGuard并根据上一步生成的manifest_keep.txt文件内容去压缩class,剔除没有用到的class,生成一个精简的jar包buidl/intermediates/multi-dex/${variant.dirName}/componentClasses.jar

1
create{variant}MainDexClassList task

这个task会根据上一步生成的componentClasses.jar去寻找这里面的各个class文字中依赖的class,比如一个class中有一成员变量X,那么X就是依赖的class,componentClasses.jar中所有的class和依赖的class路径都会被写入到文件buidl/intermediates/multi-dex/${variant.dirName}/maindexlist.txt中,这个文件中的类都会被编译进主的classes.dex中去。(详情可以查看ClassReferenceListBuilder的实现源码

NoClassDefFoundError

Multidex固然是好的,不用再为方法数超过65536而苦恼了。但是有时往往会带来意想不到的bug,比如NoClassDefFoundError。之前我就在项目中遇到了这个问题,一启动程序就crash了,看log是由于某个类找不到引起的。

通过上面的分析,我们已经得知Multidex的原理了,所以要解决一启动程序就NoClassDefFoundError的问题只需要确定该类是否正确被编译到主classes.dex中去了,如果没有被编进去的话,只要修改下maindexlist.txt文件,把这个类添加进去即可。由于maindexlist.txt这个文件是每次编译时自动生成的,手动去修改它是没用的,所以我们可以在gradle编译中新加入一个task,在create{variant}MainDexClassList这个task完成之后再去修改maindexlist.txt文件添加丢失的class

原创不易,欢迎转载,但还请注明出处:waynell.github.io