Gradle For Android系列5:多模块构建管理

Android Studio不仅可以为应用和库创建模块,还可以为Android Wear,Android TV,Google App Engine等创建模块。 所有这些模块可以在单个项目中一起使用。 例如,你可能需要创建一个应用,该应用使用Google Cloud Endpoints作为后端,并包含与Android Wear的集成。 在这种情况下,你可以拥有一个包含三个不同模块的项目:一个用于应用程序,一个用于后端,一个用于Android Wear集成。 了解多模块项目的结构和构建可以显着加快你的开发周期。

Gradle和Gradle Android插件的文档都使用多项目构建(multiproject builds)的术语。 但是,在Android Studio中,模块和项目之间有区别。 模块指的是一个单独应用:例如Android应用程序或Google App Engine后端。而一个项目(Project)是所有模块的集合。在本书中,我们使用术语模块项目与IDE的方式相同,以避免混淆。 在浏览文档时请记住这一点。

在本章中,我们将介绍多模块构建的理论,然后展示一些在实际开发中有用的例子:

  • 解析多模块构建
  • 向项目添加模块
  • 最佳实践

解析多模块构建

通常,项目根目录下包含多个子模块项目来组织多模块项目。要告诉Gradle项目是如何结构化的,哪些目录包含模块,你需要在项目的根目录中提供一个settings.gradle文件。然后每个模块都可以提供自己的build.gradle文件。我们已经在第2章“基本构建自定义”中了解了settings.gradle和build.gradle文件的工作方式,因此这里我们将仅关注如何将它们用于多模块项目。

多模块项目结构目录如下:

1
2
3
4
5
6
7
project
|-setting.gradle
|-build.gradle
|-app
|-buid.gradle
|-library
|-build.gradle

这是设置具有多模块项目最简单和最直接的方法。 settings.gradle文件声明项目中所有模块,其内容如下所示:

1
include ':app', ':library'

确保应用程序和库模块包含在构建配置中。你只需要添加模块的目录的名称到settings文件中。

要将库模块添加为应用程序模块的依赖,你需要将其添加到应用程序模块的build.gradle文件:

1
2
3
dependencies {
compile project(':library')
}

为了对模块添加依赖,您需要使用project()方法,
以模块路径作为参数。

如果要使用子目录来组织模块,可以配置Gradle以满足你的需求。 例如,你可以具有如下所示的目录结构:

1
2
3
4
5
6
7
8
9
10
project
|-setting.gradle
|-build.gradle
|-app
|-build.gradle
|-libraries
|-library1
|-build.gradle
|-library2
|-build.gradle

应用程序模块跟原来一样还是位于项目根目录下,但项目现在有两个不同的库模块。这些库模块不位于项目的根目录,但在一个特定的libraries目录。有了这个目录结构,你可以在这样settings.gradle声明应用程序和库模块:

1
include ':app', ':libraries:library1', ':libraries:library2'

注意到在子目录中声明模块是很容易的。 所有路径都相对于根目录(settings.gradle文件所在的位置)。 冒号用作路径中的正斜杠的替换。

当在子目录中将模块作为依赖关系添加到另一个模块时,应该始终从根目录引用它。 这意味着,如果上一个示例中的应用程序模块依赖于library1,应用程序模块的build.gradle文件应如下所示:

1
2
3
dependencies {
compile project(':libraries:library1')
}

如果在子目录中声明依赖关系,所有路径应该仍然相对于根目录。 这样做的是因为Gradle从项目的根目录开始构建项目的依赖关系模型。

Build生命周期回顾

了解构建过程模型是如何构建的可以让你更容易理解多模块项目的组成原理。 我们已经在第1章 开始使用Gradle和Android Studio中讨论了构建生命周期,因此你已经了解了基础知识,但是一些细节对于多模块构建尤其重要。

在第一阶段,初始化阶段,Gradle寻找一个settings.gradle文件。如果此文件不存在,Gradle会假定你只有一个构建模块。如果你有多个模块,则需要在setting文件中定义包含各个模块的目录。如果这些子目录包含自己的build.gradle文件,Gradle将处理这些子目录,并将它们合并到构建过程模型中。这就解释了为什么你在app模块依赖library模块时,依赖的路径需要相对于根路径 Gradle将总是尝试从根路径中找出依赖。

一旦你了解了构建过程模型是如何组合在一起的,有几个策略来配置多模块项目构建。你可以在根目录中的build.gradle文件中配置所有模块的构建过程。这使得很容易得到一个项目的整个构建配置的概述,但它可能会变得非常混乱,特别是当你有模块,需要不同的插件,每个都有自己的DSL的时候。另一种方法是为每个模块分别建立build.gradle文件。该策略确保模块彼此不紧密耦合。它还使得更容易跟踪构建更改,因为您不需要确定哪个更改适用于哪个模块。

最后一个策略是混合方法。您可以在项目根目录中创建一个构建文件,以定义所有模块的公共属性,以及每个模块的构建文件,以配置仅应用于该特定模块的设置。 Android Studio遵循此方法。它在根目录中创建一个build.gradle文件,并为模块创建另一个build.gradle文件。

模块任务

一旦你的项目中有多个模块,你就需要在运行任务之前考虑两次。当你在命令行界面中从项目根目录运行任务时,Gradle将确定哪些模块具有任务并为每个模块执行它。例如,如果你有一个移动设备
应用模块和一个Android Wear模块,运行gradlew assembleDebug将构建移动应用程序模块和Android Wear模块的调试版本。但是,当你将目录更改为其中一个模块时,Gradle将只运行该特定模块的任务,即使你在项目的根目录中使用Gradle包装器。例如,从Android Wear模块目录运行../gradlew assembleDebug只会构建Android Wear模块。

切换目录以运行特定于模块的任务可能会令人厌烦。幸运的是,还有另一种方法。你可以使用模块名称 + 任务名称,实现在该特定模块上运行任务。例如,要仅构建Android Wear模块,可以使用gradlew :wear :assembleDebug命令。

向项目添加模块

通过在Android Studio中执行添加新模块。会出现添加向导,向还可以设置构建过程。在某些情况下,添加模块可能会导致Android Studio编辑应用程序模块的构建文件。例如,添加Android Wear模块时,IDE会假设你想要在Android应用中使用它,会在构建文件的依赖块中添加一行来依赖Android Wear模块。

这是Android Studio中的New Module对话框的样子:

在以下部分中,将展示如何将不同模块添加到Android Studio中,顺带解释其自定义属性,并指定如何更改构建过程。

添加Java Library模块

当你添加新的Java库模块时,Android Studio生成的build.gradle文件如下所示:

1
2
3
4
5
apply plugin: 'java'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}

Java库模块使用Java插件,而不是我们常用的Android插件。这意味着很多Android特定的属性和任务不可用,在Java库中,你不也需要那些任务。

构建文件还具有基本的依赖关系管理设置,因此你可以将JAR文件添加到libs文件夹,而无需任何特殊配置。 你可以使用第3章“管理依赖关系”中学到的内容添加更多依赖关系。依赖关系配置不依赖于Android插件。

例如,要将名为javalib的Java库模块添加为应用程序模块的依赖关系。只需将此行添加到应用程序模块的构建配置文件中:

1
2
3
dependencies {
compile project(':javalib')
}

Gradle会在构建中导入一个名为javalib的模块。如果在应用程序模块中添加此模块的依赖,那么javalib模块将始终在应用程序模块构建之前先行构建。

添加Android Library模块

Android Library的默认build.gradle文件从这行开始:

1
apply plugin: 'com.android.library'

在Android Library模块上添加依赖关系的方法与Java库完全相同:

1
2
3
dependencies {
compile project(':androidlib')
}

Android Library不仅包含Java代码,而且还包含所有Android资源,例如manifest文件,string和layout。 在应用中引用Android Library后,你可以使用Libarary中的所有的class和resource。

集成Android Wear

如果你想将应用程序深度整合到Android Wear中,就必须新增Android Wear模块。但需要注意的是,Android Wear模块也使用Android application plugin。这意味着application的所有构建属性和任务都可用。

build.gradle文件与常规Android应用程序模块不同的唯一部分是依赖性配置:

1
2
3
4
5
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.support:wearable:1.1.0'
compile 'com.google.android.gms:play-services-wearable:6.5.87'
}

每个Android Wear应用都依赖于Google提供的几个Android Wear专用库。要在Android应用中依赖Android Wear应用,你可以在Android应用中加入wear库的依赖:

1
2
3
dependencies {
wearApp project(':wear')
}

以上配置可确保Wear模块在进行必要的设置后将wear模块的APK被添加到Android应用程序的APK中。

使用Google App Engine

原文中有较详细的介绍,因为被墙,所以暂时不作介绍

最佳实践

接下来介绍几种方法,这些方法可以更容易地处理多模块项目;在使用多个模块时还有一些注意事项,本节会详细介绍。

在Android Studio中运行模块的gradle任务

正如我们在[第2章“基本构建定制”]中看到的,可以在Android Studio直接运行Gradle任务。当你有多个模块时,Android Studio也会识别它们,并分组显示所有可用任务及相关描述。

Gradle工具窗口使运行特定模块的Gradle任务变得更加容易。但是问题是没有选项可以同时运行所有模块的任务了,所以你的需求是希望Gradle任务同时在所有模块上生效,使用命令行还是更快一些。

加快多模块编译速度

当你构建多模块项目时,Gradle会按顺序处理所有模块。 随着多核计算机越来越多,你可以设置并行构建来加快构建速度。此功能在Gradle中已存在,但默认情况下未启用。

如果要对项目应用并行构建执行,则需要在项目根目录下的gradle.properties文件中配置parallel属性:

1
org.gradle.parallel=true

Gradle会根据可用的CPU内核数选择正确的线程数。为了防止在同一模块并行执行而产生并发问题,每个线程都拥有一整个模块。

模块耦合

正如我们在第2章“基本构建定制”中看到的,可以使用build.gradle文件中的allprojects定义项目中所有模块的属性。当你具有包含多个模块的项目时,可以使用任何模块中的allprojects将属性应用于项目中的所有模块。一个模块甚至可以引用另一个模块的属性。这些强大的功能可以使多模块构建的维护更容易。但缺点是,你的模块变得耦合。

一旦两个模块访问对方的任务或属性,就认为它们是耦合的。这有几个后果,例如,模块变得难以移植,当你试图将某一个项目引用到其他项目中时,因为模块间的耦合,导致抽离出模块很困难;模块耦合对并行构建也有影响,在模块中使用allprojects块将使并行构建执行无效。以上两点都需要注意。

你可以通过不直接访问其他模块的任务或属性来避免耦合。通过使用根模块作为中介,使得模块仅耦合到根模块,而不是彼此耦合。

总结

这一章,我们研究了如何在单个项目中设置多个模块, 还了解到了添加新模块会影响构建任务的运行方式。

然后,我们查看了一些添加新模块的实际示例,以及如何将每个模块集成到一个项目中。 最后,我们提到了一些优化技巧,使得在一个项目中使用多个模块更容易。

在下一章中,我们将设置各种测试,并了解如何使用Gradle来更容易运行这些测试。我们将直接在Java虚拟机上查看运行单元测试,也可以在真机或模拟器上运行测试。

写得好,就打赏一下吧!