Android的编译过程
在了解这个问题之前我们先要来看看Android 应用编译的过程:
- IDE中的资源打包工具 (Android Asset Packaging Tool ,即图中的aapt) 会将应用中的资源文件进行编译,这些资源文件包括
AndroidManifest.xml
文件,为Activity定义的 XML 文件等等。在这个编译过程中也会产生一个R.java
文件,这样你就可以在你的Java代码中引用这些资源了。 - aidl 工具会将你项目中的所有
.aidl
接口转换成Java接口。 - 项目中的所有的Java代码,包括
R.java
和.aidl
文件,都会被Java编译器编译,然后输出 .class 文件。 - 接着 dex 工具就会把上一步骤产生的 .class 文件转成 Dalvik 字节码,也就是
.dex
文件。同时项目中包含的所有第三方类库和 .class 文件也会被转换成.dex
文件,这样讲方便下一步被打包成最终的.apk
文件。 - 所有的不能编译的资源(比如图片等等)、编译后的资源文件和 .dex 文件会被 apkbuilder 工具打包成一个
.apk
文件。 - 一旦
.apk
文件被构建好之后,如果要把把它安装到设备上面去的话,它就必须用一个debug 或者发行key来对这个apk文件签名。 - 最后,如果应用程序已经被签名成为发行模式的apk,你还需要使用 aipalign工具对
.apk
进行对齐优化。这样的话可以减少应用程序在设备上的内存消耗。
为什么会有这个Dex 方法限制
内部原因:
我们注意到在第四步的时候,会产生一个.dex
文件。Android 从之前的Dalvik 到现在Android 5.0 默认的ART 运行时环境都能够执行这个.dex
文件,它们还使用同一套指令集,即Dalvik 指令集。通过这篇关于Android 指令集格式的介绍文章中,我可以知道Dalvik 指令集是使用16位寄存器来保存项目中所有的方法引用,包括第三方的方法:
invoke-kind {vC, vD, vE, vF, vG}, meth@BBBB B: method reference index (16 bits)
这就意味着 Android的单个.dex
文件最能引用65536个方法,在这之后的方法就无法引用了。这就是Android Dex 方法限制异常出现的原因,同时因为ART和Dalvik使用同一套指令集,这个限制在ART 运行时环境中也会存在。
外部原因:
第三方库里面包含太多的方法。这里就拿Google Play Service和Guava来举例。很多Android开发者都会用到Google Play Service库和Guava库,而你知道它们提供了多少了方法吗?Google Play Service 5.0里面就差不多包含了将近20k+方法,Guava提供了将近14k个方法。这个两个库就将近占了方法限制数目65536的半壁江山。
那么如何解决Android Dex 方法限制这个问题呢?
老方法
对于内部原因:
- 从上面的描述中我们知道,Android Dex 方法限制是出现在单个
.dex
文件中的,那么我们可以在一个apk中使用多个.dex
文件吗?可以,Android 官方博客就给出了这个方案。(在Android5.0之前,由于大部分使用的是Dalvik 运行时环境,Dalvik 运行时环境限制一个apk只能包含一个classes.dex文件。)
对于外部原因:
新方法(官方动作)
主要思路:使用multidex support library 让Android5.0之前的版本也能在一个apk里面包含多个.dex
文件。具体使用方法请参看这篇文章。
Google不仅在工具上面做出了改进,还把自己的Google Play Service库也做了一番改动——从Google Play Service 6.5开始开始支持更细精度的依赖管理,也就是说你只需要Google Drive的api,而不需要google game,maps或者wallet等api的支持,那你就可以只引入Google Drive的api即可。这样可以在很大程度上减少Dex 方法限制出现的几率。
参考链接:
- http://developer.android.com/tools/building/index.html
- http://android-developers.blogspot.com/2011/07/custom-class-loading-in-dalvik.html
- https://medium.com/@rotxed/dex-skys-the-limit-no-65k-methods-is-28e6cb40cf71
- http://developer.android.com/tools/building/multidex.html
- jakewharton.com/play-services-is-a-monolith/
- http://www.keysolutions.com/blogs/kenyee.nsf/d6plinks/KKYE-9LP5ND
- http://www.alittlemadness.com/2010/06/07/understanding-the-android-build-process/
- http://source.android.com/devices/tech/dalvik/dalvik-bytecode.html
- http://stackoverflow.com/questions/21490382/does-the-android-art-runtime-have-the-same-method-limit-limitations-as-dalvik/21492160#21492160
- https://android.googlesource.com/platform/dalvik/+/froyo/vm/analysis/ReduceConstants.c
《“关于 Android Dex 方法限制的一些总结”》 有 4 条评论
学习了。很不错的东西,说的很详细!
讲的太浅
我自己看了些资料 然后总结了一下。很多详细的讲解都在参考链接里面了。
当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised
Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。但是在早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536.