关于 Android Dex 方法限制的一些总结

Android的编译过程

在了解这个问题之前我们先要来看看Android 应用编译的过程:

build

  1. IDE中的资源打包工具 (Android Asset Packaging Tool ,即图中的aapt) 会将应用中的资源文件进行编译,这些资源文件包括 AndroidManifest.xml文件,为Activity定义的 XML 文件等等。在这个编译过程中也会产生一个 R.java 文件,这样你就可以在你的Java代码中引用这些资源了。
  2. aidl 工具会将你项目中的所有 .aidl 接口转换成Java接口。
  3. 项目中的所有的Java代码,包括 R.java.aidl 文件,都会被Java编译器编译,然后输出 .class 文件。
  4. 接着 dex 工具就会把上一步骤产生的 .class 文件转成 Dalvik 字节码,也就是 .dex 文件。同时项目中包含的所有第三方类库和 .class 文件也会被转换成 .dex 文件,这样讲方便下一步被打包成最终的 .apk 文件。
  5. 所有的不能编译的资源(比如图片等等)、编译后的资源文件和 .dex 文件会被 apkbuilder 工具打包成一个 .apk 文件。
  6. 一旦 .apk 文件被构建好之后,如果要把把它安装到设备上面去的话,它就必须用一个debug 或者发行key来对这个apk文件签名。
  7. 最后,如果应用程序已经被签名成为发行模式的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 方法限制这个问题呢?

老方法

对于内部原因:

  1. 从上面的描述中我们知道,Android Dex 方法限制是出现在单个.dex 文件中的,那么我们可以在一个apk中使用多个.dex 文件吗?可以,Android 官方博客就给出了这个方案。(在Android5.0之前,由于大部分使用的是Dalvik 运行时环境,Dalvik 运行时环境限制一个apk只能包含一个classes.dex文件。)

对于外部原因:

  1. 使用配置脚本对第三方库中的方法进行清除;
  2. 使用ProGuard清除项目中无用的方法,不过效果不如上面的。

 

新方法(官方动作)

主要思路:使用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个想法

  1. 当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised
    Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。但是在早期的Android系统中,DexOpt有一个问题,DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536.

发表评论

电子邮件地址不会被公开。 必填项已用*标注