我和我的团队最近在我们的应用程序中达到了 64K 方法引用,这是 dex 文件中支持的最大数量。为了绕过这个限制,我们需要将程序的一部分分割成多个二级dex文件,并在运行时加载它们。
我们按照问题中提到的旧的、基于 Ant 的构建系统的博客文章进行操作,一切正常。但我们最近觉得有必要迁移到基于 Gradle 的新构建系统。
此答案并不打算用完整的示例替换完整的博客文章。相反,它将简单地解释如何使用 Gradle 来调整构建过程并实现相同的目标。请注意,这可能只是其中一种方式,也是我们目前在团队中的方式。这并不一定意味着它是唯一的方法。
我们的项目结构略有不同,这个示例作为一个单独的 Java 项目工作,它将所有源代码编译成 .class 文件,将它们组装成一个 .dex 文件,最后,将该单个 .dex 文件打包成一个 .jar文件。
开始吧...
在根build.gradle中,我们有以下代码来定义一些默认值:
ext.androidSdkDir = System.env.ANDROID_HOME
if(androidSdkDir == null) {
Properties localProps = new Properties()
localProps.load(new FileInputStream(file('local.properties')))
ext.androidSdkDir = localProps['sdk.dir']
}
ext.buildToolsVersion = '18.0.1'
ext.compileSdkVersion = 18
我们需要上面的代码,因为尽管该示例是一个单独的 Java 项目,但我们仍然需要使用 Android SDK 中的组件。稍后我们还将需要其他一些属性......所以,在主项目的build.gradle上,我们有这个依赖项:
dependencies {
compile files("${androidSdkDir}/platforms/android-${compileSdkVersion}/android.jar")
}
我们还简化了这个项目的源代码集,这对于您的项目可能不是必需的:
sourceSets {
main {
java.srcDirs = ['src']
}
}
接下来,我们将内置jar
任务的默认配置更改为仅包含classes.dex文件而不是所有 .class 文件:
configure(jar) {
include 'classes.dex'
}
现在我们需要有一个新任务,它将所有 .class 文件实际组装成一个 .dex 文件。在我们的例子中,我们还需要将 Protobuf 库 JAR 包含到 .dex 文件中。所以我在这里的例子中包括了它:
task dexClasses << {
String protobufJarPath = ''
String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? '.bat' : ''
configurations.compile.files.find {
if(it.name.startsWith('protobuf-java')) {
protobufJarPath = it.path
}
}
exec {
commandLine "${androidSdkDir}/build-tools/${buildToolsVersion}/dx${cmdExt}", '--dex',
"--output=${buildDir}/classes/main/classes.dex",
"${buildDir}/classes/main", "${protobufJarPath}"
}
}
此外,请确保您在build.gradle文件的某处(当然通常在顶部)有以下导入:
import org.apache.tools.ant.taskdefs.condition.Os
现在我们必须使jar
任务依赖于我们的dexClasses
任务,以确保我们的任务在最终的 .jar 文件组装之前执行。我们用一行简单的代码来做到这一点:
jar.dependsOn(dexClasses)
我们完成了......只需使用通常的assemble
任务调用 Gradle,您的最终 .jar 文件${buildDir}/libs/${archivesBaseName}.jar
将包含一个classes.dex文件(除了 MANIFEST.MF 文件)。只需将其复制到您的应用程序资产文件夹中(您始终可以像我们所做的那样使用 Gradle 自动执行该操作,但这超出了此问题的范围)并按照博客文章的其余部分进行操作。
如果您有任何问题,请在评论中大声疾呼。我会尽我所能提供帮助。