我是https://github.com/creativepsyco/secondary-dex-gradle/的维护者,我是一个gradle n00b,所以我选择了BASH脚本的路径,虽然我认为可以直接在构建文件中完成。或者可以重构为作为插件运行,当我能够接受 Gradle 时,我可能会这样做。这是我的逻辑的原因。
要了解如何拆分 DEX,您必须了解构建系统任务顺序。如果您使用的是 gradle,那么您必须知道在构建周期中注入了一系列任务。
例如:
:sdk:processReleaseJavaRes UP-TO-DATE
:sdk:packageReleaseJar
:sdk:compileReleaseNdk UP-TO-DATE
:sdk:packageReleaseJniLibs UP-TO-DATE
:sdk:packageReleaseLocalJar UP-TO-DATE
:sdk:packageReleaseRenderscript UP-TO-DATE
:sdk:packageReleaseResources UP-TO-DATE
:sdk:bundleRelease
:app:prepareComAndroidSupportAppcompatV71910Library UP-TO-DATE
:app:prepareComFacebookAndroidFacebook3141Library UP-TO-DATE
:app:prepareDebugDependencies
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets UP-TO-DATE
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:processDebugManifest UP-TO-DATE
:app:processDebugResources UP-TO-DATE
:app:generateDebugSources UP-TO-DATE
:app:compileDebugJava
:app:preDexDebug
:app:dexDebug
:app:processDebugJavaRes UP-TO-DATE
:app:validateReleaseConfigSigning
:app:packageDebug
:app:zipalignDebug
:app:assembleDebug
为了进行 Dexing,您应该能够在 dex* 和 process* 任务之间注入您的自定义任务。如果你能做到这一点,那么多重 DEXing 就变得容易了。
Bash 脚本here 基本上就是这样做的,如果你检查调试任务,它基本上会:
- 将库 Jar 文件获取到 dex,通常它是特定于构建的并且存在于 Android 库的
exploded-aar 文件夹中并在其上运行 DEX 工具
- 将其复制到 assets 文件夹中,该文件夹位于最终要打包到应用程序中的 libs 文件夹中
- 所有库资源等都已合并,这意味着需要再次解压缩文件。
在gradle build script
// For Debug simply remove the library from getting dex and create it
//----------------------- Extra Debug Step ----------------//
def libraryFiles = new ArrayList<?>()
def secondaryFile = new ArrayList<?>()
variant.dex.libraries.each {
File file ->
if (!file.absolutePath.contains("lib/unspecified/classes.jar")) {
libraryFiles.add(file)
} else {
secondaryFile.add(file)
}
}
variant.dex.libraries = libraryFiles
//----------------------- Extra Debug Step ----------------//
packagingTask.dependsOn variant.javaCompile
}
这会手动将库从 dexed 中移除,以便可以通过 bash 脚本生成。
我认为你可以用同样的方法在发布过程中找出 dexing。另一个需要注意的重要事情是 Proguard 任务是由 android gradle 插件控制的,你不能改变太多。 Proguard 规则的问题:
- proguard 的每次传递都是不同的,我们不希望最终出现我们的 2 个 DEX 具有不同的 proguard 映射的情况
- 这使我们处于无法保护库的情况,但这并不是真正可取的。
- 必须在 proguard 之后生成 dex 文件以确保映射相同。 Gradle 不支持 Proguard 之后的 assets 合并(我们希望将 dex 文件放在 assets 文件夹中)
另一个重要的代码块位于 SecondaryDex.java 中,它实质上加载了第二个 dex 文件并将 DEX 文件的路径注入运行时类路径。您可以对此进行优化,只需注入路径,而不是每次恢复应用程序时读取 DEX 文件。
我在 Google Play Services 上做了二次 Dex 实验(增加了 20K 方法),并且能够分离成一个单独的 DEX 文件。这样我的主 dex 文件就不会受到 Google Play 服务膨胀的影响。
要了解 Gradle 任务循环是如何工作的,您可以参考 BasePlugin.groovy 源代码,您可以看到在访问变体对象和构建任务的适当 API 之前很难控制某些方面。