现在Gradle也支持Kotlin了,很多人本来就使用Kotlin开发Android,现在用一种语言同时可以把build脚本的工作也做了,可谓一举两得。
Kotlin DSL
DSL即Domain-Specific Language。
Kotlin与Groovy一样是DSL友好的语言,所以很适合用于Gradle脚本编写。
Kotlin版本的Gradle文件后缀会发生变化, ks
即Kotlin Script
的意思:
.gradle > .gradle.kts
接下来我们学习如何将gradle文件改成Kts的写法:
环境
我们以一个既有Gradle项目为例:
- Android Studio 3.5.3
- Gradle 6.0.1
- Kotlin 1.3.61
- JDK 8
将各个gradle
文件后缀名改为gradle.kts
,并用kotlin重写其内容
gradle > gradle.kts
settings.gradle > settings.gradle.kts
include(":app")
rootProject.name = "MyApplication"
build.gradle -> build.gradle.kts (root)
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:3.5.3")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61")
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task<Delete>("clean") {
delete(rootProject.buildDir)
}
build.gradle -> build.gradle.kts (module)
plugins {
id("com.android.application")
kotlin("android")
kotlin("android.extensions")
}
android {
compileSdkVersion(29)
buildToolsVersion = "29.0.0"
defaultConfig {
applicationId = "<ApplicationId>"
minSdkVersion(23)
targetSdkVersion(29)
versionCode = 1
versionName = "0.1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61")
implementation("androidx.appcompat:appcompat:1.1.0")
implementation("androidx.core:core-ktx:1.2.0-rc01")
implementation("androidx.constraintlayout:constraintlayout:2.0.0-beta2")
testImplementation("junit:junit:4.12")
androidTestImplementation("androidx.test.ext:junit:1.1.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.2.0")
}
sync并等待成功结束
依赖管理
当项目有多个module且都依赖了相同的库时,库的版本升级或者替换等会导致多个gradle.kts
文件同时修改。此时可以通过将依赖库的配置在buildSrc
中集中管理,减少重复修改的次数:
- 在项目根目录创建
buildSrc/
文件夹,用来存放依赖库的配置。
以普通文件夹的形式创建 (New > Directory > buildSrc
),不需要Add Module
- Sync Now
sync中…成功。buildSrc
目录下会生成build
和.gradle
- 创建
buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}
- 创建依赖库配置文件
创建目录buildSrc/src/main/kotlin/depende/
,在目录下创建dependencies/Dep.kt
,用对象类定义全局静态常量,在其他gradle.kts
中可以像调用ext
一样调用到这些kt
中的静态常量。
object Dep {
object Plugin {
val android = "com.android.tools.build:gradle:3.5.3"
val kotlin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Kotlin.version}"
val safeArgs = "androidx.navigation:navigation-safe-args-gradle-plugin:${AndroidX.Navigation.version}"
}
object Test {
val junit = "junit:junit:4.12"
val androidJunit = "androidx.test.ext:junit:1.1.1"
val espressoCore = "androidx.test.espresso:espresso-core:3.2.0"
}
object Kotlin {
const val version = "1.3.61"
val stdlibJdk = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$version"
}
object AndroidX {
val appCompat = "androidx.appcompat:appcompat:1.1.0"
val coreKtx = "androidx.core:core-ktx:1.2.0-rc01"
val constraint = "androidx.constraintlayout:constraintlayout:2.0.0-beta2"
}
}
定义dependencies/Packages.kt
,用来存放Android相关的配置
object Packages {
const val id = "<Application Id>"
const val debugIdSuffix = ".debug"
object SdkVersion {
const val target = 29
const val compile = 29
const val min = 21
}
object Version {
private const val major = 0
private const val minor = 1
private const val build = 0
val code = (major * 100 + minor * 10 + build)
val name = "$major.$minor.$build"
}
}
值得一提的是,受益于JVM文件之间可以互相调用的特性,普通的gradle
文件也可以调用到kt
文件中的Dep
或者Packages
,实际上groovy
和kt
在配置Gradle时是可以共存的,我们这里只介绍在gradle.kts
中的写法,.gradle
中的写法大家可以自己实践,效果是类似的。
- Sync again
sync中…成功! - 我们在
gradle.kts
文件中import上述kt
文件
import dependencies.Packages
import dependencies.Dep
android {
compileSdkVersion(Packages.SdkVersion.compile)
buildToolsVersion = Packages.SdkVersion.compile.toString()
defaultConfig {
applicationId = Packages.id
minSdkVersion(Packages.SdkVersion.min)
targetSdkVersion(Packages.SdkVersion.target)
versionCode = Packages.Version.code
versionName = Packages.Version.name
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
implementation(Dep.Kotlin.stdlibJdk)
implementation(Dep.AndroidX.appCompat)
implementation(Dep.AndroidX.coreKtx)
implementation(Dep.AndroidX.constraint)
testImplementation(Dep.Test.junit)
androidTestImplementation(Dep.Test.androidJunit)
androidTestImplementation(Dep.Test.espressoCore)
}
同样,修改根目录的gradle.kts
import dependencies.Dep
buildscript {
dependencies {
classpath(Dep.Plugin.android)
classpath(Dep.Plugin.kotlin)
}
}
- Sync again
sync中…成… 出错了?
org.gradle.internal.exceptions.LocationAwareException: Build file '../build.gradle.kts' line: 10
Script compilation errors:
Line 10: classpath(Dep.Plugin.android)
^ Unresolved reference: Dep
Line 11: classpath(Dep.Plugin.kotlin)
^ Unresolved reference: Dep
经过一番摸索,下面两种方式中的任一种修改后,可以成功sync
- 根目录的
build.gradle.kts
中引用的静态变量的定义放到src/main/kotlin/
- 将根目录
build.gradle.kts
改回build.gradle
原因不清楚,有清楚的大佬欢迎留言指教
总结
推荐大家以后改用kotlin配置gradle,至少会带来以下好处:
- 无需额外学习Groovy
- 编辑Gradle文件时IDE支持Kotlin智能补全,Groovy则不支持
- Kotlin提供顶级变量吗,可以通过
Dep
等全局静态变量传递配置,Groovy则要借助Java的成员变量Project#ext
传递配置 - 错误信息易的读性比Groovy要好得多