Gradle 是用于 Android 应用程序开发的构建工具,可以帮助开发者管理项目的编译,打包,签名等任务,同时支持模块化开发,定制构建流程和多种签名配置,简化了项目管理和提高了开发效率。现在 Android 官方已经默认推荐使用 Kolin DSL 来构建 Gradle 了,那就一起来瞧瞧吧!
初始配置
新建一个项目,看看初始配置。
我们从上到下来看一看,相关配置的说明都在注释里。
Module 下的 build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
//应用程序的命名空间,主要用于访问应用程序资源。
namespace = "com.xzj.myapp"
//编译所依赖的 Android SDK 版本。
compileSdk = 33
defaultConfig {
//APP 的唯一标识
applicationId = "com.xzj.myapp"
//支持的最低 API Level,如指定23,表示低于 Android 6.0 的机型不能使用这个 APP。
minSdk = 23
//基于哪个 Android 版本开发的,如指定33,代表适配到 Android 13。
targetSdk = 33
//版本号
versionCode = 1
//版本名称
versionName = "1.0"
//指定运行测试时要使用的 InstrumentationRunner,AndroidJUnitRunner 是 Android 测试框架中的一个组件,用于执行针对 Android 应用程序的测试。
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
//表示在项目中使用支持库来处理矢量图像
vectorDrawables {
useSupportLibrary = true
}
}
//构建类型,主要用于打包。
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
//指定 Java 环境版本
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
//指定 JVM 环境
kotlinOptions {
jvmTarget = "1.8"
}
//是否启用 compose
buildFeatures {
compose = true
}
//指定了 Compose 所使用的 Kotlin 编译器扩展的版本
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}
//表示在打包过程中排除项目中位于 "/META-INF/AL2.0" 和 "/META-INF/LGPL2.1" 路径下的资源文件
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
//依赖
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.activity:activity-compose:1.7.0")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
gradle-wrapper.properties
#Thu Mar 21 18:07:31 CST 2024
#Gradle 压缩包解压后的主目录
distributionBase=GRADLE_USER_HOME
#Gradle 压缩包解压后的具体路径
distributionPath=wrapper/dists
#Gradle 的下载地址
distributionUrl=https://services.gradle.org/distributions/gradle-8.0-bin.zip
#存放 Gradle zip 压缩包的主目录。
zipStoreBase=GRADLE_USER_HOME
#存放 Gradle zip 压缩包的具体路径
zipStorePath=wrapper/dists
Project 下的 build.gradle.kts
//使用 Kotlin DSL 配置 Gradle 插件
plugins {
id("com.android.application") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
}
gradle.properties
#设置 JVM 的最大堆内存为 2048MB,指定了文件编码为 UTF-8。
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
#指示项目使用 AndroidX 库
android.useAndroidX=true
#指定 Kotlin 代码风格的规范,official 表示采用官方推荐的代码风格。
kotlin.code.style=official
#用于控制R类是否会传递依赖,当为 true 时,表示类不会被传递到依赖模块中,每个模块会有自己独立的 R 类。
android.nonTransitiveRClass=true
local.properties
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/opt/android/sdk
settings.gradle.kts
//插件管理,指定插件下载的仓库。
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
//依赖管理,指定依赖库下载的仓库,此仓库是有顺序的,顺序决定先从哪个仓库下载。
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
//项目名称
rootProject.name = "MyApp"
//指定参与构建的模块,一个根工程可以有很多的 module,只有在这设置了,gradle 才会去识别,才会在构建的时候被包含进去。
include(":app")
生命周期
Task 是 Gradle 构建的核心,生命周期的意义就是在各个阶段把 Task 组合起来,按照我们的意图去构建项目。 Gradle 的生命周期可以分为三个阶段:
- 初始化阶段:Gradle 支持单项目和多项目构建,在初始化阶段,Gradle 确定哪些项目将参与构建,并为每个项目创建 Project 实例,比如解析 settings.gradle 文件,以此来决定是单项目构建还是多项目构建。
- 配置阶段:解析每个工程的 build.gradle 文件,创建要执行的任务子集和确定各种任务之间的关系,并对任务做一些初始化配置。
- 运行阶段:Gradle 根据配置阶段创建和配置的要执行的任务子集,执行任务。
Gradle 也可以监听各个阶段的回调
gradle.addProjectEvaluationListener(object : ProjectEvaluationListener {
//项目配置被评估之前触发,在这个阶段,Gradle 将会加载项目的构建脚本并准备执行项目的配置。
override fun beforeEvaluate(project: Project) {
println("beforeEvaluate")
}
//项目配置被评估之后触发,在这个阶段,所有的项目配置已经被加载和评估完成。
override fun afterEvaluate(project: Project, state: ProjectState) {
println("afterEvaluate")
}
})
当项目配置被评估完成后,Gradle 就可以准确地知道项目的结构和需要执行的任务,从而能够开始真正的构建过程。因此,在项目配置被评估之前和之后,你可以通过监听相应的事件,在项目配置生命周期的不同阶段执行自定义的逻辑。
gradle.addBuildListener(object : BuildListener {
override fun settingsEvaluated(settings: Settings) {
println("settingsEvaluated")
}
override fun projectsLoaded(gradle: Gradle) {
println("projectsLoaded")
}
override fun projectsEvaluated(gradle: Gradle) {
println("projectsEvaluated")
}
override fun buildFinished(result: BuildResult) {
println("buildFinished")
}
})
Gradle 是构建工具本身,而 gradlew 是 Gradle Wrapper 提供的便捷工具,用于在项目中管理和执行 Gradle 构建任务。在 Android 项目中,通常使用 Gradle Wrapper 来统一团队的构建环境,简化项目的配置和维护。
依赖管理
Gradle 提供了强大的依赖管理支持,我们只需声明依赖项即可,Gradle 会帮我们找到所需的 Library,例如依赖项配置和依赖仓库。
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.activity:activity-compose:1.7.0")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
需要注意的是,implementation 表示当前引用的第三方库仅限于本 Module 使用,api 表示的依赖是可以传递的。
Gradle 依赖管理进行了优化,如果项目存在同一个依赖库的多个版本,默认选择最高版本,Gradle 会自动排除重复的依赖,这在一定程度上防止了依赖冲突,但是为什么有时还是会遇到依赖冲突的问题呢?举个例子,某些第三方库可能会包含特定版本的某个库,并且你自己的项目中也引入了该库的不同版本,这种情况下,Gradle 会自动选择最高的版本,就可能发生依赖冲突。可以通过如下两种方式解决依赖冲突:
使用 exclude 排除依赖
implementation("com.google.android.exoplayer:exoplayer:2.14.0") {
exclude(group = "com.google.guava", module = "guava")
}
使用强制版本
configurations.all {
resolutionStrategy {
force("com.google.android.exoplayer:exoplayer:2.14.0")
}
}
Task
Task 是 Gradle 构建的核心对象,我们可以直接在 build.gradle 文件中创建。
创建 Task
tasks.register("xzj") {
println("create task")
}
也可以这样创建
task("xzj") {
println("create task")
}
这里需要注意的是:如果有 Task 同名的话,会编译失败。
自定义 Task
继承 DefaultTask,Action 的方法需要添加 @TaskAction 注解。
open class MyTask : DefaultTask() {
@get:Internal
var taskName = "taskName"
@TaskAction
fun action1() {
println("action1: $taskName")
}
@TaskAction
fun action2() {
println("action2: $taskName")
}
@TaskAction
fun action3() {
println("action3: $taskName")
}
}
注册自定义的 Task
tasks.register<MyTask>("myTask") {
taskName = "xzj"
}
然后我们就可以在 AndroidStudio 的 Gradle 工具面板,Tasks -> other 里找到这个 Task,双击执行即可。
如果是带构造函数传参的话,可以这样
open class MyTask @Inject constructor(private var taskName: String) : DefaultTask() {
@TaskAction
fun action1() {
println("action1: $taskName")
}
@TaskAction
fun action2() {
println("action2: $taskName")
}
@TaskAction
fun action3() {
println("action3: $taskName")
}
}
tasks.register<MyTask>("myTask","argument")
使用 @Inject 注解可以帮助 Gradle 正确地理解带参数的构造函数,并且在创建任务实例时能够正确地调用带参数的构造函数。
doFirst 和 doLast
使用了 doFirst 和 doLast 方法来分别添加在任务执行前和执行后需要执行的动作。
task("hello") {
doFirst {
println("doFirst1")
}
doFirst {
println("doFirst2")
}
println("hello")
doLast {
println("doLast1")
}
doLast {
println("doLast2")
}
}
执行这个 Task,输出如下:
doFirst 是倒序执行,doLast 是正序执行,Action 也是正序执行。
当你定义一个任务时,任务的配置代码会在 Gradle 构建脚本解析执行阶段就会执行,而不是在任务实际执行时才执行。因此,当 Gradle 解析执行到 println("hello") 这行代码时,会立即执行该代码并先打印出 hello。
dependsOn
dependsOn 用来定义任务之间的依赖关系,通过使用 dependsOn,可以确保一个任务在另一个任务之前执行,从而控制任务的执行顺序。举个例子,有两个任务 A 和 B,想让任务 B 在任务 A 执行完成后再执行,这样干:
tasks.register("taskA") {
doLast {
println("Running task A")
}
}
tasks.register("taskB") {
dependsOn("taskA")
doLast {
println("Running task B")
}
}
执行 taskB 会先执行 taskA
finalizedBy
dependsOn 指定的是上一个任务,而 finalizedBy 指定下一个任务,比如想让任务 A 执行后,再去执行任务 B,这样干:
tasks.register("taskA") {
finalizedBy("taskB")
doLast {
println("Running task A")
}
}
tasks.register("taskB") {
doLast {
println("Running task B")
}
}
onlyIf
onlyIf 是一个条件断言方法,用于指定任务是否应该执行的条件。当条件为 true 时,任务才会执行,否则任务将被跳过。
tasks.register("taskA") {
val flag = providers.gradleProperty("flag")
onlyIf { //如果属性存在,则返回 true
flag.isPresent
}
doLast {
println("Running A")
}
}
enabled
enable 是 Task 的开关,禁用之后任何操作都不会执行,可以实现和 onlyIf 一样的效果。
tasks.register("taskA") {
enabled = true
doLast {
println("Running A")
}
}
查找 Task
tasks.findByName("taskA")?.doFirst {
println("taskA findByName")
}
tasks.findByPath("taskA")?.doFirst {
println("taskA findByPath")
}
tasks.named("taskA") {
doLast {
println("taskA named")
}
}
文件操作
open class WriteFileTask : DefaultTask() {
//任务输入参数
@Input
var text = ""
//任务输出文件
@OutputFile
var outputFile: File? = null
//任务运行时调用的方法
@TaskAction
fun writeText() {
outputFile?.createNewFile()
outputFile?.writeText(text)
}
}
tasks.register("writeFileTask", WriteFileTask::class) {
text = "content"
outputFile = File(projectDir, "myFile.txt")
}
这里创建了一个写文件的 Task,执行之后就可以在相应的目录中看到创建的文件。