
@TOC
作为Android开发习惯了面向对象编程,习惯了IDEA提供的各种辅助开发快捷功能。
那么带有陌生的常规语法的Groovy脚本对于我来说一向敬而远之。
Kotlin DSL的出现感觉是为了我们量身定做的,因为采用 Kotlin 编写的代码可读性更高,并且 Kotlin 提供了更好的编译时检查和 IDE 支持。
<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">
名词概念解释
Maven.JVM byte code, 兼容Java平台.Domain Specific Language, 领域特定语言.Gradle的API是Java的,Groovy DSL是在其之上的脚本语言. Groovy DS脚本文件后缀: .gradle.在讨论从 Groovy 迁移时,术语“KTS”和“Kotlin DSL”可以互换使用。换句话说,“将 Android 项目从 Groovy 转换为 KTS”与“将 Android 项目从 Groovy 转换为 Kotlin DSL”实际上是一个意思。
类型 | Kotlin | Groovy |
|---|---|---|
自动代码补全 | 支持 | 不支持 |
是否类型安全 | 是 | 不是 |
源码导航 | 支持 | 不支持 |
重构 | 自动关联 | 手动修改 |
Kotlin, 开发者可能对这个语言更熟悉更喜欢.IDE支持更好, 自动补全提示, 重构,imports等.Kotlin是静态类型.KTS 的构建速度可能比采用 Groovy 慢(自测小demo耗时增加约40%(约8s))。Project Structure 编辑器不会展开在 buildSrc 文件夹中定义的用于库名称或版本的常量。KTS 文件目前在项目视图中不提供文本提示。Groovy 字符串可以用单引号 'string' 或双引号 "string" 引用,而 Kotlin 需要双引号 "string"。Groovy 允许在调用函数时省略括号,而 Kotlin 总是需要括号。Gradle Groovy DSL 允许在分配属性时省略 = 赋值运算符,而 Kotlin 始终需要赋值运算符。所以在KTS中需要统一做到:


Groovy DSL 脚本文件使用 .gradle 文件扩展名。
Kotlin DSL 脚本文件使用 .gradle.kts 文件扩展名。
由于您可以在项目中结合使用 Groovy build 文件和 KTS build 文件,因此将项目转换为 KTS 的一个简单方法是先选择一个简单的 build 文件(例如 settings.gradle),将其重命名为 settings.gradle.kts,然后将其内容转换为 KTS。之后,确保您的项目在迁移每个 build 文件之后仍然可以编译。
由于Koltin 是静态类型语言,Groovy是动态语言,前者是类型安全的,他们的性质区别很明显的体现在了 task 的创建和配置上。详情可以参考Gradle官方迁移教程
// groovy
task clean(type: Delete) {
delete rootProject.buildDir
}
// kotiln-dsl
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}
val clean by tasks.creating(Delete::class) {
delete(rootProject.buildDir)
}open class GreetingTask : DefaultTask() {
var msg: String? = null
@TaskAction
fun greet() {
println("GreetingTask:$msg")
}
}
val msg by tasks.creating(GreetingTask::class) {}
val testTask: Task by tasks.creating {
doLast {
println("testTask:Run")
}
}
val testTask2: Task = task("test2") {
doLast {
println("Hello, World!")
}
}
val testTask3: Task = tasks.create("test3") {
doLast {
println("testTask:Run")
}
}plugins 代码块如果您在build 文件中使用 plugins 代码块,IDE 将能够获知相关上下文信息,即使在构建失败时也是如此。IDE 可使用这些信息执行代码补全并提供其他实用建议,从而帮助您解决 KTS 文件中存在的问题。
在您的代码中,将命令式 apply plugin 替换为声明式 plugins 代码块。Groovy 中的以下代码…
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'androidx.navigation.safeargs.kotlin'在 KTS 中变为以下代码:
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt")
id("androidx.navigation.safeargs.kotlin")
}如需详细了解 plugins 代码块,请参阅 Gradle 的迁移指南。
注意:plugins 代码块仅解析 Gradle 插件门户中提供的插件或使用 pluginManagement 代码块指定的自定义存储库中提供的插件。如果插件来自插件门户中不存在的 buildScript 依赖项,那么这些插件在 Kotlin 中就必须使用 apply 才能应用。例如:
apply(plugin = "kotlin-android")
apply {
from("${rootDir.path}/config.gradle")
from("${rootDir.path}/version.gradle.kts")
}如需了解详情,请参阅 Gradle 文档。
强烈建议您
plugins {}优先使用块而不是apply()函数。有两个关键的最佳实践可以更轻松地在
Kotlin DSL的静态上下文中工作:使用
plugins {}块将本地构建逻辑放在构建的buildSrc目录中
该plugins {}块是关于保持您的构建脚本声明性,以便充分利用
Kotlin DSL。使用buildSrc项目是关于将您的构建逻辑组织成共享的本地插件和约定,这些插件和约定易于测试并提供良好的 IDE 支持。
常见依赖
// groovy
implementation project(':library')
implementation 'com.xxxx:xxxx:8.8.1'
// kotlin
implementation(project(":library"))
implementation("com.xxxx:xxx:8.8.1")freeTree
// groovy
implementation fileTree(include: '*.jar', dir: 'libs')
//kotlin
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))特别类型库依赖
//groovy
implementation(name: 'splibrary', ext: 'aar')
//kotlin
implementation (group="",name="splibrary",ext = "aar")buildTypes在 Kotlin DSL 中,某些 buildTypes(如 debug 和 release,)是隐式提供的。但是,其他 buildTypes 则必须手动创建。
例如,在 Groovy 中,您可能有 debug、release 和 staging buildTypes:
buildTypes
debug {
...
}
release {
...
}
staging {
...
}在 KTS 中,仅 debug 和 release buildTypes 是隐式提供的,而 staging 则必须由您手动创建:
buildTypes
getByName("debug") {
...
}
getByName("release") {
...
}
create("staging") {
...
}Grovvy编写:
productFlavors {
demo {
dimension "app"
}
full {
dimension "app"
multiDexEnabled true
}
}
buildTypes {
release {
signingConfig signingConfigs.signConfig
minifyEnabled true
debuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
debuggable true
}
}
signingConfigs {
release {
storeFile file("myreleasekey.keystore")
storePassword "password"
keyAlias "MyReleaseKey"
keyPassword "password"
}
debug {
...
}
}kotlin-KTL编写:
productFlavors {
create("demo") {
dimension = "app"
}
create("full") {
dimension = "app"
multiDexEnabled = true
}
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
isDebuggable = false
proguardFiles(getDefaultProguardFile("proguard-android.txtt"), "proguard-rules.pro")
}
getByName("debug") {
isMinifyEnabled = false
isDebuggable = true
}
}
signingConfigs {
create("release") {
storeFile = file("myreleasekey.keystore")
storePassword = "password"
keyAlias = "MyReleaseKey"
keyPassword = "password"
}
getByName("debug") {
...
}
}我们通常会把签名信息、版本信息等配置写在gradle.properties中,在kotlin-dsl中我们可以通过一下方式访问:
rootProject.extra.propertiesproject.extra.propertiesrootProject.propertiespropertiesSystem.getProperties()
System.getProperties()使用的限制比较多
systemProp.xxx格式(例如:systemProp.kotlinVersion=1.3.72);> Configure project :buildSrc和> Configure project :的结果不同,后者无法获取的gradle.properties中的数据);获取工程的local.properties文件
gradleLocalProperties(rootDir)
gradleLocalProperties(projectDir)
val JAVA_HOME:String = System.getenv("JAVA_HOME") ?: "default_value"
Google 官方推荐的一个 Gradle 配置最佳实践是在项目最外层 build.gradle 文件的ext代码块中定义项目范围的属性,然后在所有模块间共享这些属性,比如我们通常会这样存放依赖的版本号。
// build.gradle
ext {
compileSdkVersion = 28
buildToolsVersion = "28.0.3"
supportLibVersion = "28.0.0"
...
}但是由于缺乏IDE的辅助(跳转查看、全局重构等都不支持),实际使用体验欠佳。
在KTL中用extra来代替Groovy中的ext
// The extra object can be used for custom properties and makes them available to all
// modules in the project.
// The following are only a few examples of the types of properties you can define.
extra["compileSdkVersion"] = 28
// You can also create properties to specify versions for dependencies.
// Having consistent versions between modules can avoid conflicts with behavior.
extra["supportLibVersion"] = "28.0.0"android {
// Use the following syntax to access properties you defined at the project level:
// rootProject.extra["property_name"]
compileSdkVersion(rootProject.extra["sdkVersion"])
// Alternatively, you can access properties using a type safe delegate:
val sdkVersion: Int by rootProject.extra
...
compileSdkVersion(sdkVersion)
}
...
dependencies {
implementation("com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}")
...
}
build.gralde中的ext数据是可以在build.gradle.kts中使用extra进行访问的。
val abiCodes = mapOf("armeabi-v7a" to 1, "x86" to 2, "x86_64" to 3)
android.applicationVariants.all {
val buildType = this.buildType.name
val variant = this
outputs.all {
val name =
this.filters.find { it.filterType == com.android.build.api.variant.FilterConfiguration.FilterType.ABI.name }?.identifier
val baseAbiCode = abiCodes[name]
if (baseAbiCode != null) {
//写入cpu架构信息
variant.buildConfigField("String", "CUP_ABI", "\"${name}\"")
}
if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
//修改apk名称
if (buildType == "release") {
this.outputFileName = "KotlinDSL_${name}_${buildType}.apk"
} else if (buildType == "debug") {
this.outputFileName = "KotlinDSL_V${variant.versionName}_${name}_${buildType}.apk"
}
}
}
}我们在使用Groovy语言构建的时候,往往会抽取一个version_config.gradle来作为全局的变量控制,而ext扩展函数则是必须要使用到的,而在我们的Gradle Kotlin DSL中,如果想要使用全局控制,则需要建议使用buildSrc。
复杂的构建逻辑通常很适合作为自定义任务或二进制插件进行封装。自定义任务和插件实现不应存在于构建脚本中。buildSrc则不需要在多个独立项目之间共享代码,就可以非常方便地使用该代码了。
buildSrc被视为构建目录。编译器发现目录后,Gradle会自动编译并测试此代码,并将其放入构建脚本的类路径中。
先创建
buildSrc目录; 在该目录下创建build.gradle.kts文件; 创建一个buildSrc/src/main/koltin目录; 在该目录下创建Dependencies.kt文件作为版本管理类;
需要注意的是buildSrc的build.gradle.kts:
plugins {
`kotlin-dsl`
}
repositories {
jcenter()
}或者
apply {
plugin("kotlin")
}
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath(kotlin("gradle-plugin", "1.3.72"))
}
}
//dependencies {
// implementation(gradleKotlinDsl())
// implementation(kotlin("stdlib", "1.3.72"))
//}
repositories {
gradlePluginPortal()
}不同版本之间buildSrc下的build.gradle文件执行顺序:
gradle-wrapper.properties:5.6.4
com.android.tools.build:gradle:3.2.0
BuildSrc:build.gradlesetting.gradleProject:build.gradleMoudle:build.gradle
gradle-wrapper.properties:6.5
com.android.tools.build:gradle:4.1.1
setting.gradleBuildSrc:build.gradleProject:build.gradleMoudle:build.gradle所以在非buildSrc目录下的build.gradle.kts文件中我们使用Dependencies.kt需要注意其加载顺序。
Android官网-将构建配置从 Groovy 迁移到 KTS
Migrating build logic from Groovy to Kotlin
GitHub:kotlin-dsl-samples/samples/hello-android
Kotlin DSL: Gradle scripts in Android made easy
文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦~!~!
想阅读作者的更多文章,可以查看我 个人博客
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。