众所周知,开发Android应用时,随着应用越来越大,迟早会遇到超过65535方法数的问题。虽然Google 给出了避免64K 方法数的解决方案,但是并不完美。
比如我们最近遇到的。在自动分包后,因为classes.dex太大,出现了在2.3设备上安装失败的现象。此时,adb 报的错误是 INSTALL_FAILED_DEXOPT 。这个现象的原因是 LinearAlloc 在 4.0 之前最多只能分配5M的空间。在4.0以后是8M或16M。而这个5M的大小和 classes.dex 文件本身的大小无关,与复杂性有关。有人给出的经验是如果方法数在 56000 以下,一般就可以在 5M 以内。(参考 https://ingramchen.io/blog/2014/09/prevention-of-android-dex-64k-method-size-limit.html)
对于这个问题,我是这样解决的。
-
使用 dx 命令把一些jar包转成 classes2.dex,比如
dx --dex --output="ui/build_scripts/ext_dex/classes2.dex" "ui/libs2"
-
生成classes2.dex的jar只参与编译,不参与生成classes.dex。使用
provided fileTree(dir: 'libs2', include: ['*.jar'])
替代
compile fileTree(dir: 'libs2', include: ['*.jar'])
-
build过程中打包classes2.dex到apk文件中
写了个简单的脚本,其实可以用 gradle task 实现吧,把 classes2.dex 复制到classes.dex所在目录,而且添加一些自定义task:
def overseaAutoMultidex = !buildOversea // copy ext dex def generateCopyDex2Task(baseName) { return tasks.create("copyDex2TaskFro${baseName}", Exec) { println 'begin copy classes2.dex' workingDir = 'build_scripts' commandLine 'python3', 'cp_ext_dex.py' doLast { String output = standardOutput.toString() println output } } }
def overseaDebugCopyTask = generateCopyDex2Task("Debug") def overseaStagingCopyTask = generateCopyDex2Task("Staging") def overseaReleaseCopyTask = generateCopyDex2Task("Release") afterEvaluate() { if (!overseaAutoMultidex) { // 不同的工程,具体task名称不同,一般是 packageDebug // assembleRelease,transformClassesWithDexForDebug, transformClassesWithDexForRelease 等 packageOverseaDebug.dependsOn overseaDebugCopyTask overseaDebugCopyTask.shouldRunAfter transformClassesWithDexForOverseaDebug packageOverseaStaging.dependsOn overseaStagingCopyTask overseaStagingCopyTask.shouldRunAfter transformClassesWithDexForOverseaStaging assembleOverseaRelease.dependsOn overseaReleaseCopyTask overseaReleaseCopyTask.shouldRunAfter transformClassesWithDexForOverseaRelease } }
-
利用 multidex.jar 安装 classes2.dex,在 MyApplication 里调用下面的语句还是不能少的。
MultiDex.install(ctx);
这种方式有个很明显的缺点,就是参与生成classes2.dex的jar包不能与其他jar包一起使用proguard压缩。所以,这些jar包一般是第三方的jar包,已经proguard过的。