管理多模块构建

Android studio不仅可以为应用和依赖库创建模块,还可以为Android Wear,Android TV和Google App Engine等创建模块。所有这些模块都可以在一个单一的项目中使用。例如,你可能想创建一个使用Google Cloud Endpoints作为后台,融合Android Wear的应用。在这种情况下,你的项目需要包含三个不同模块:一个针对App,一个针对后端,还一个针对Android Wear的整合,了解多模块项目的结构和构建可以显著加快开发周期。

解剖多模块构建

通常,一个多模块项目有一个根目录,在其子文件夹中包含所有的模块。为了告知Gradle,项目的结构以及哪个文件夹包含哪些模块,你需要在项目的根目录提供一个settings.gradle文件。每个模块都可以提供自己的build.gradle文件,这是建立多模块项目最简单,最直接的方式。在项目的settings.gradle文件中声明了所有的模块,如下所示:
include ‘:app’,’:library’
该代码确保了app和library模块包含在构建配置中。你需要做的就是添加模块的目录名。
如果想给app模块添加library模块作为一个依赖,那么你需要将它添加到app模块的build.gradle文件中:
dependencies{
compile project(’:library’)
}
为了在一个模块中添加一个依赖,你需要使用带有模块路劲作为参数的project()方法。

如果你想使用子文件夹来管理你的模块,那么Gradle也已通过配置满足了你的需求如:
在这里插入图片描述

app模块依然像之前一样位于根目录,但现在该项目有两个不同的library,并且这些模块不在根目录下,而是位于一个特定的library目录。通过这个目录结构,你可以在settings.gradle中声明app和library模块:

include ‘:app’,’:libraries:library1’,’:libraries:library2’

注意,在一个子目录中声明模块十分简单。所有的路径都是相对于根目录的(settings.gradle文件的位置)。冒号被用来替代路径中的斜线。

当一个子目录中添加一个模块作为另一个模块的依赖时,你应该总是从根目录引用他。这意味着如果前面例子中的app模块依赖library1,那么app模块的build.gradle文件应该像这样:

dependencies{
compile project(’:libraries:library1’)
}

如果你在子目录中声明了依赖,那么所有路劲都会相对于根目录。这样做的原因是,Gradle是从项目的根目录开始创建项目依赖模型的。

重访构建生命周期

了解了构建进程模型是如何构造的之后,会让理解多模块项目的组成变得更加容易。

在第一阶段,即初始化阶段,Gradle搜寻settings.gradle文件。如果该文件不存在,Gradle则假设你只有一个单独的模块构建。如果你有多个模块,那么你可以在settings文件中定义子目录来包含各个模块。如果这些子目录有其自己的build.gradle文件,则Gradle会处理,并把他们合并到构建进程模型。这就解释了为什么你应该相对于根目录的路径来声明模块上的依赖。因为Gradle始终尝试从根目录找到依赖。

一旦你理解了构建过程模型是如何工作的,就会知道配置多模块项目构建策略的方式有以下几种。他们可以在根目录的build.gradle文件中配置所有模块的设置。这让概览一个项目的整个构建配置变得容易,但是其也会变得凌乱,特别是当你的模块需要不同的插件,而他们的插件有其自己的DSL时。另一种方式是每个模块都有一个独立的build.gradle文件。该策略可以确保模块不紧密耦合,也使得跟踪构建变化变得容易,因为你不需要找到哪些改变应用了哪些模块。

最后一种策略是混合的做法。你可以在项目的根目录中设置一个构建文件,用来定义所有模块的通用属性,然后每个模块的构建文件用于配置只针对该模块的设置。Android Studio遵循了这一策略。其会在根目录创建一个build.gradle文件,然后再每个模块内创建build.gradle文件。

模块任务

只要你的项目中有多个模块,那么在你运行任务之前,就需要三思而行。当你在项目根目录下的命令行界面运行一个任务时,Gradle会找出哪个模块有这一名称的任务,然后为每个模块执行该任务。举个例子,如果你有一个手机app模块和一个Android Wear模块,那么运行gradlew assembleDebug将会构建手机app模块和Android wear模块的debug版本。当你切换到其中一个模块的根目录时,Gradle将只会针对该模块运行任务,即便你在项目的根目录中还在使用Gradle wrapper。

切换目录来进行模块特定的任务时会让人十分恼火。幸运的是,还有另一种途径。你可以在一个任务名称之前加上模块名,用来在特定的模块上运行该task。例如,为了之构建Android Wear模块,你可以使用gradlew :wear:assembleDebug命令。

在Android Studio中运行模块任务

我们可以在Android Studio中直接运行Gradle任务。当有多个模块时,Android Studio可以自动识别他们,并且显示说有可用任务的分组情况,如下:

在这里插入图片描述

加速多模块构建

当构建一个多模块项目时,Gradle会按照顺序处理所有的模块。随着计算机提供越来越多的内核,我们可以通过并行运行所有模块来使得构建过程更快,此功能已存在于Gradle中,但默认是不开启的。如果你想并行构建适用于你的项目,那么你需要在项目根目录的gradle.properties文件中配置parallel属性:
org.gradle.parallel=true
Gradle会基于可用的CPU内核,来选择正确的线程数量。为了防止出现同一模块同时执行两个任务的问题,每个线程只拥有一个完整的模块。

每个人的构建时间可能都会有所不同,但通过点单启动并行构建,你可以从你的构建中省下大量的时间。需要注意的是,为了是并行工作有效,你需要保证你的模块之间是分离的。

模块耦合

通过在build.gradle文件中使用allprojects,我们可以在一个项目中定义所有模块的属性。当你的项目有多个模块时,你可以在任何模块中使用allprojects来应用属性到项目的所有模块中。Gradle甚至允许一个模块引用另一个模块的属性。这些强大的功能,是的维护多模块构建更加方便。不足之处是你的模块耦合在一起了。

只要两个模块互相访问了对方的任何或属性,就认为这两个模块是耦合的。耦合会造成几个后果。例如,你放弃了可移植性。一旦你决定从项目中提取出一个library,那么再复制所有项目范围的设置之前,你将不能运行该library。模块耦合对并行构建也有所影响。在任一模块中使用allprojects,都将使得并行构建变得无用。当你给所有模块添加项目范围的属性时,需要注意这一点。

你可以通过不直接从其他模块访问任务或属性来避免耦合。如果你需要这么做,那么可以使用根模块作为中介,这样模块就只与根模块耦合,而不是和其他模块耦合。

原创文章 63 获赞 59 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013049016/article/details/92569343