Gradle笔记_5-管理多模块构建
5.1 解剖多模块构建
通常,一个多模块项目有一个根目录,在其子文件夹中包含所有的模块。
为了告知 Gradle 项目的结构以及哪个文件夹包含那些模块,需要在项目的根目录提供一个 settings.gradle 文件。
每个模块都可以提供自己的 build.gradle 文件。
下面是多模块项目结构:
在项目的 settings.gradle 文件中声明了所有的模块,如下所示:
include ':app', ':library'
该代码确保了 app 和 library 模块包含在构建配置中。
给 app 模块添加 library 模块作为一个依赖,需要在 app 模块的 build.gradle 文件中添加 library 依赖:
dependencies {
implementation project(':library')
}
如果使用子文件夹管理你的模块,Gradle 也可以通过配置满足你的要求:
在 settings.gradle 中将模块添加到构建:
include ':libraries:library2'
include ':libraries:library1'
include ':library'
include ':app'
或
include ':app', ':library', ':libraries:library1', ':libraries:library2'
注意,在一个子目录中声明模块,所有的路径都是相对于根目录的(settings.gradle 文件所在目录)。冒号被用来替代路径中的斜线。
将一个子目录中的模块添加为依赖是,总是需要从根目录引用它。例如 app 模块需要依赖上面 libraries 下的 library1 模块,需要在 app 模块的 build.gradle 文件中添加:
dependencies {
implementation project(path: ':libraries:library1')
// 或 implementation project(':libraries:library1')
}
如果在子目录中声明了依赖,那么所有的路径都会相对于根目录。这样做的原因是 Gradle 是从项目的根目录开始创建项目依赖模型的。
5.1.1 重访构建生命周期
源码所述:
Project 和 build.gradle(Project#DEFAULT_BUILD_FILE)文件有一个一一对应关系,即每一个 build.gradle 文件对应一个 Project 对象。在构建的初始化阶段,Gradle 为每一个参与构建的工程(模块)组装一个 Project 对象,如下:
- 为构建创建一个
org.gradle.api.initialization.Settings
实例。 - 搜寻(源码用的 evaluate 评估) settings.gradle 脚本,如果存在,让
org.gradle.api.initialization.Settings
对象配置它。 - 通过配置好的
org.gradle.api.initialization.Settings
对象创建 Project 实例(Project instances)层级结构。 - 最后,针对每一个工程(模块)通过执行 build.gradle 文件为每一个 Project 评估。按广度评估,以便一个工程先于它的子工程被评估。
《Gradle for Android》书中所述:
- 第一阶段,即初始化阶段,Gradle 搜寻 settings.gradle 文件。
- 文件不存在,Gradle 则假设你只有一个单独的模块构建。
- 有多个模块,根据 settings.gradle 文件中声明模块位置,Gradle 会处理各个模块的 build.gradle 文件,并把它们合并到构建进程模型。
Gradle 始终尝试从根目录找到依赖。
配置多模块项目构建策略的方式有以下几种:
- 在项目根目录的 build.gradle 文件中配置所有模块的设置。
简单,但会非常凌乱,特别当各个模块需要不同的插件,他们的插件又有其自己的 DSL 时。 - 每一个模块都有一个独立的 build.gradle 文件。
可确保模块不紧密耦合,也使得跟踪构建变化变得容易,因为你不需要找到哪些改变应用了哪些模块。 - 混合做法。在项目的根目录的 build.gradle 中定义所有模块通用的属性,在每个模块的 build.gradle 中配置只针对该模块的设置。
5.1.2 模块任务
当你在项目根目录下的命令行界面运行一个任务时,Gradle 会找出哪个模块有着一名名称的任务,然后为每个模块执行该任务。
你可以:
切换模块目录,运行 …/gradlew assembleDebug,【不成功】- 在任务名称之前加上模块名
$ gradlew :library:assembleDebug
、$ gradlew :libraries:library1:assembleDebug
5.2 将模块添加到项目
Android Studio 的 New Module 对话框:
5.2.1 添加一个 Java 依赖库
File > New > New Module > Select a Module Type 弹窗 > 选择 Java or Kotlin Library
添加一个新的 Java 依赖模块后,Android Studio 生成的 build.gradle 将如下所示:
// apply plugin: 'java'
plugins {
id 'java-library'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation fileTree(dir: 'libs', includes: ['*.jar'])
}
Java 依赖库模块使用的是 Java 插件,而不是之前的 Android 插件。
其构建文件也有基本的依赖管理设置,可以在 libs 文件夹添加 JAR 文件。依赖配置不依赖于 Android 插件。
例如添加一个名为 javalib 的 Java module 作为依赖库,在 settings.gradle 中,加上(通过 Android Studio 的 New Module 创建时,会自动添加):
include ':javalib'
给 app 模块添加 javalib 模块的依赖,只需要在构建文件中添加一行代码:
dependencies {
implementation project(path: ':javalib')
}
该代码告诉 Gradle 在构建的时候引入一个名为 javalib 的模块。如果在你的 app 模块添加了该依赖,那么 javalib 模块将始终在构建 app 模块之前构建。
5.2.2 添加一个 Android 依赖库
File > New > New Module > Select a Module Type 弹窗 > 选择 Android Library
创建一个 Android 依赖库,默认 build.gradle 文件开始于下面这行代码:
apply plugin: 'com.android.library'
// 使用新版 Android Studio v4.1.2 如下
//plugins {
// id 'com.android.library'
//}
添加 Android 依赖库依赖 给 app 模块,与添加 Java 依赖库方式完全相同:
dependencies {
// implementation project(':androidlib')
implementation project(path: ':androidlib')
}
一个 Android 依赖库不仅包含依赖库中的 Java 代码,还包含所有的 Android 资源,如 manifest、strings、drawables 和 layouts 等。一旦在你的 app 中引入了一个 Android 依赖库,那么你就可以在该 app 中使用依赖库的所有类和资源了。
执行构建任务组装 APK 时,会合并所有模块的资源。
融合 Android Wear
略过
适用 Google App Engine
略过
5.3 提示和最佳实践
5.3.1 在 Android Studio 中运行模块任务
你可以在 Android Studio 中直接运行 Gradle 任务。当有多个模块时,Android Studio 可以识别它们,并显示所有可用任务的分组情况。
Gradle 工具窗让运行模块特有的任务变得更加容易。同时针对所有模块运行一个任务,还是需要使用命令行,也更快些。
5.3.2 加速多模块构建
当创建一个多模块项目时,Gradle 会顺序处理所有的模块。我们可以通过并行运行所有模块来使得构建过程更快。
此功能在 Gradle 中默认不开启。需要在项目根目录的 gradle.properities 文件中配置 parallel 属性:
org.gradle.parallel=true
Gradle 会基于可用的 CPU 内核来选择正确的线程数。
注意:为了使冰箱工作有效,你需要确保你的模块之间是分离的。
5.3.3 模块耦合
- 通过在 build.gradle 文件中使用
allprojects
,我们可以在一个项目中定义所有模块的属性。 - 当你的项目有多个模块时,你可以在任何模块中使用
allprojects
来应用属性到项目的所有模块中。 - Gradle 甚至允许一个模块引用另一个模块的属性。
这些强大的功能,使得维护多模块构建更加方便。不足之处是模块耦合在一起了。
只要两个模块互相访问了对方的任务和属性,就认为这两个模块间是耦合的。耦合会造成几个后果:
- 放弃了可移植性。当需要提取一个模块,需要复制涉及的资源和设置,耦合越严重,提取越困难。
- 对并行构建有影响。在任一模块中使用 allprojects,都将使得并行构建失效。
避免模块耦合:
- 不直接从其他模块访问任务或属性来避免耦合。可以使用根模块作为中介,这样模块就只与根模块耦合而不是其他模块耦合。