Android Groovy 迁移到 KTS

Groovy 迁移到 KTS

概述

Android Studio是使用Gradle来编译,而默认的构建语言是Groovy,但是Gradle实际上是支持Kotlin来编写Gradle构建脚本的,常见的构建脚本是.gradle结尾,而Koltin语法编写的脚本则是.gradle.kts 。

官方文档

主要修改以下配置文件:

  • settings.gradle
  • project/build.gradle
  • app/build.gradle

迁移流程

setting.gradle

以前代码:

rootProject.name = "My Application"
include ':app'

修改为:

rootProject.name = "My Application"
include(":app")

project/build.gradle

以前代码:

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:${Deploy.gradleVersion}")
        classpath(kotlin("gradle-plugin", Deploy.kotlinVersion))
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

tasks {
    val clean by registering(Delete::class) {
        delete(buildDir)
    }
}

修改为:

buildscript {
    val kotlinVersion = "1.5.31"
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:4.2.2")
        
//        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
        //上面可简写为:
        classpath(kotlin("gradle-plugin", kotlinVersion))
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        jcenter()
    }
}

tasks {
    val clean by registering(Delete::class) {
        delete(buildDir)
    }
}

module/build.gradle

在 Gradle 的 Kotlin DSL(KTS)中,register 和 create 都是用于创建和配置对象的方法,但它们的行为有所不同。

  • register:这个方法会创建一个新的对象,但不会立即配置它。配置会在对象被实际需要时(即懒加载)进行。这意味着如果你的构建脚本中没有使用到这个对象,那么它就不会被创建和配置,从而节省了构建时间。
  • create:这个方法会立即创建和配置对象。这意味着无论你的构建脚本中是否使用到这个对象,它都会被创建和配置。 在大多数情况下,你应该优先使用 register,因为它可以提高构建性能。然而,如果你需要立即访问和修改新创建的对象,那么你应该使用 create。

以前代码:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.0"

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 22
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    signingConfigs {
        debug {
            storeFile file('./myjks.jks')
            storePassword "123456"
            keyAlias "app"
            keyPassword "123456"
        }
        release {
            storeFile file('./myjks.jks')
            storePassword "123456"
            keyAlias "app"
            keyPassword "123456"
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.1'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

修改为:

plugins {
    id("com.android.application")
    
//    id("kotlin-android")
    //上面可简写为:
    kotlin("android")
}

android {
    compileSdkVersion(30)
    buildToolsVersion("30.0.3")

    defaultConfig {
        applicationId = "com.example.myapplication"
        minSdkVersion(22)
        targetSdkVersion(30)
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    // 配置签名
    signingConfigs {
        getByName("debug") {
            storeFile = file("./myjks.jks")
            storePassword = "123456"
            keyAlias = "app"
            keyPassword = "123456"
        }
        register("release") {
            storeFile = file("./myjks.jks")
            storePassword = "123456"
            keyAlias = "app"
            keyPassword = "123456"
        }
    }

    // 编译类型
    buildTypes {
        getByName("debug") {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
            signingConfig = signingConfigs.getByName("debug")
        }
        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
            signingConfig = signingConfigs.getByName("release")
        }
    }

    // 输出文件名称
    android.applicationVariants.all {
        val buildType = this.buildType.name
        outputs.all {
            if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
                if (buildType == "debug") {
                    this.outputFileName = "app_V${defaultConfig.versionName}_${buildType}.apk"
                } else if (buildType == "release") {
                    this.outputFileName = "app_V${defaultConfig.versionName}_${buildType}.apk"
                }
            }
        }
    }

    // 部署资源文件
    fun listSubFile(): ArrayList<String> {
        // 新资源目录
        val resFolder = "src/main/res/layouts"
        // 新资源目录下的文件夹
        val files = file(resFolder).listFiles()
        val folders = ArrayList<String>()
        // 遍历路径
        files?.let {
            it.forEach { file ->
                folders.add(file.absolutePath)
            }
        }
        // 资源整合
        folders.add(file(resFolder).parentFile.absolutePath)
        return folders
    }

    // 资源重定向
    sourceSets {
        getByName("main") {
            res.srcDirs(listSubFile())
        }
    }

    // 指定JDK
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }

    // 开启ViewBinding
    buildFeatures {
        viewBinding = true
    }
}

dependencies {
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*jar"))))
    implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.31")
    implementation("androidx.core:core-ktx:1.3.1")
    implementation("androidx.appcompat:appcompat:1.2.0")
    implementation("com.google.android.material:material:1.2.1")
    implementation("androidx.constraintlayout:constraintlayout:2.0.1")
    testImplementation("junit:junit:4.+")
    androidTestImplementation("androidx.test.ext:junit:1.1.2")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.3.0")
    implementation("com.google.code.gson:gson:2.10.1")
}

处理ext扩展函数

在使用 Groovy 语言构建的时候,一般都会抽取一个 config.gradle 来作为全局的变量控制,而ext扩展函数则是必须要使用到的,而在我们的 Gradle Kotlin DSL 中,如果想要使用全局控制,则需要 buildSrc。

buildSrc官网

依次创建如下目录和文件

在这里插入图片描述

build.gradle.kts:

plugins {
    `kotlin-dsl`
}

repositories {
    jcenter()
}

Versions.kt:

object Versions {
    //Android
    const val appCompat = "1.2.0"
    const val constraintlayout = "2.0.1"
    const val junit = "4.+"
    const val testExt = "1.1.2"
    const val espresso = "3.3.0"
    const val lifecycle = "2.5.0"
    const val material = "1.2.1"

    //Kotlin
    const val kotlinVersion = "1.5.31"
    const val coreKtx = "1.3.1"
    const val coroutines = "1.4.3"

    //KTX
    const val activityKtx = "1.2.3"
    const val fragmentKtx = "1.3.6"

    //other
    const val retrofit = "2.9.0"
    const val gson = "2.8.6"
    const val moshi = "1.9.3"
    const val glide = "4.11.0"
}

Libs:

object Libs {
    //Android
    const val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}"
    const val constraintlayout =
        "androidx.constraintlayout:constraintlayout:${Versions.constraintlayout}"
    const val junit = "junit:junit:${Versions.junit}"
    const val testExt = "androidx.test.ext:junit:${Versions.testExt}"
    const val espresso = "androidx.test.espresso:espresso-core:${Versions.espresso}"
    const val viewmodel = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.lifecycle}"
    const val livedata = "androidx.lifecycle:lifecycle-livedata-ktx:${Versions.lifecycle}"
    const val lifecycle = "androidx.lifecycle:lifecycle-common-java8:${Versions.lifecycle}"
    const val lifecycleRT = "androidx.lifecycle:lifecycle-runtime-ktx:${Versions.lifecycle}"
    const val material = "com.google.android.material:material:${Versions.material}"

    //Kotlin
    const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlinVersion}"
    const val coreKtx = "androidx.core:core-ktx:${Versions.coreKtx}"
    const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines}"
    const val coroutinesTest =
        "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.coroutines}"
    const val coroutinesAndroid =
        "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines}"

    //KTX
    const val activityKtx = "androidx.activity:activity-ktx:${Versions.activityKtx}"
    const val fragmentKtx = "androidx.fragment:fragment-ktx:${Versions.fragmentKtx}"

    //other
    const val gson = "com.google.code.gson:gson:${Versions.gson}"
    const val moshi = "com.squareup.moshi:moshi:${Versions.moshi}"
    const val moshiCodeGen = "com.squareup.moshi:moshi-kotlin-codegen:${Versions.moshi}"
    const val converterMoshi = "com.squareup.retrofit2:converter-moshi:${Versions.retrofit}"
    const val retrofit = "com.squareup.retrofit2:retrofit:${Versions.retrofit}"
    const val glide = "com.github.bumptech.glide:glide:${Versions.glide}"
    const val glideCompiler = "com.github.bumptech.glide:compiler:${Versions.glide}"
}

ProjectProperties:

object ProjectProperties {
    const val compileSdk = 31
    const val buildTools = "31.0.0"
    const val minSdk = 22
    const val targetSdk = 31

    const val applicationId = "com.example.myapplication"
    const val versionCode = 1
    const val versionName = "1.0.0"

    const val agpVersion = "4.2.2"
}

Deloy:

object Deloy {

    fun getSystemTime(): String {
        val dateFormat = SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA)
        return dateFormat.format(System.currentTimeMillis())
    }
}

Signing:

object Signing {
    const val StoreFile = "./myjks.jks"
    const val StorePassword = "123456"
    const val KeyAlias = "app"
    const val KeyPassword = "123456"
}
使用

project/build.gradle:

buildscript {
    repositories {
        google()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath("com.android.tools.build:gradle:${ProjectProperties.agpVersion}")

        classpath(kotlin("gradle-plugin", Versions.kotlinVersion))
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        jcenter()
        maven { url = uri("https://www.jitpack.io") }
        maven { url = uri("https://jitpack.io") }
    }
}

tasks {
    val clean by registering(Delete::class) {
        delete(buildDir)
    }
}

module/build.gradle:

plugins {
    id("com.android.application")
    id("kotlin-android")
    kotlin("android")
    kotlin("kapt")
}

android {
    compileSdkVersion(ProjectProperties.compileSdk)
//    buildToolsVersion(ProjectProperties.buildTools)

    defaultConfig {
        applicationId = ProjectProperties.applicationId
        minSdkVersion(ProjectProperties.minSdk)
        targetSdkVersion(ProjectProperties.targetSdk)
        versionCode = ProjectProperties.versionCode
        versionName = ProjectProperties.versionName

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    // 配置签名
    signingConfigs {
        getByName("debug") {
            storeFile = file(Signing.StoreFile)
            storePassword = Signing.StorePassword
            keyAlias = Signing.KeyAlias
            keyPassword = Signing.KeyPassword
        }
        register("release") {
            storeFile = file(Signing.StoreFile)
            storePassword = Signing.StorePassword
            keyAlias = Signing.KeyAlias
            keyPassword = Signing.KeyPassword
        }
    }

    // 编译类型
    buildTypes {
        getByName("debug") {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
            signingConfig = signingConfigs.getByName("debug")
        }
        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
            signingConfig = signingConfigs.getByName("release")
        }
    }

    // 输出文件名称
    android.applicationVariants.all {
        val buildType = this.buildType.name
        outputs.all {
            if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
                if (buildType == "debug") {
                    this.outputFileName = "app_V${defaultConfig.versionName}_${buildType}.apk"
                } else if (buildType == "release") {
                    this.outputFileName =
                        "app_V${defaultConfig.versionName}_${buildType}_${Deloy.getSystemTime()}.apk"
                }
            }
        }
    }

    // 部署资源文件
    fun listSubFile(): ArrayList<String> {
        // 新资源目录
        val resFolder = "src/main/res/layouts"
        // 新资源目录下的文件夹
        val files = file(resFolder).listFiles()
        val folders = ArrayList<String>()
        // 遍历路径
        files?.let {
            it.forEach { file ->
                folders.add(file.absolutePath)
            }
        }
        // 资源整合
        folders.add(file(resFolder).parentFile.absolutePath)
        return folders
    }

    // 资源重定向
    sourceSets {
        getByName("main") {
            res.srcDirs(listSubFile())
        }
    }

    // 指定JDK
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }

    // 开启ViewBinding
    buildFeatures {
        viewBinding = true
    }
}

dependencies {
    // Android
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
    implementation(Libs.appCompat)
    implementation(Libs.constraintlayout)
    implementation(Libs.viewmodel)
    implementation(Libs.livedata)
    implementation(Libs.lifecycle)
    implementation(Libs.lifecycleRT)
    implementation(Libs.material)
    testImplementation(Libs.junit)
    androidTestImplementation(Libs.testExt)
    androidTestImplementation(Libs.espresso)

    // Kotlin
    implementation(Libs.kotlinStdLib)
    implementation(Libs.coreKtx)
    implementation(Libs.coroutines)
    implementation(Libs.coroutinesTest)
    implementation(Libs.coroutinesAndroid)

    //KTX
    implementation(Libs.activityKtx)
    implementation(Libs.fragmentKtx)

    // Network
    implementation(Libs.gson)
    implementation(Libs.retrofit)
    implementation(Libs.moshi)
    implementation(Libs.converterMoshi)
    kapt(Libs.moshiCodeGen)

    // Image
    implementation(Libs.glide)
    kapt(Libs.glideCompiler)
}

源码

<think>好的,用户现在问的是在Android Studio中使用KTS还是Groovy。之前他们问过kts和grooye,我纠正过Grooye可能是Groovy的拼写错误,现在他们正确使用了Groovy,所以这次的问题更明确了。 首先,我需要确定用户的使用场景。他们可能是在配置Gradle构建脚本时遇到了选择困难,或者刚接触Android开发,对这两种DSL不太了解。用户可能是开发者,需要了解两者的区别和优劣势,以便做出合适的选择。 接下来,我要比较KTS(Kotlin DSL)和Groovy的优缺点。需要涵盖语法差异、性能、IDE支持、社区趋势等方面。还要提到迁移成本,因为用户可能已经在用Groovy,考虑是否值得切换到KTS。 用户可能关心的问题包括:哪种更容易维护,未来是否会被淘汰,有没有兼容性问题,学习曲线如何。需要指出KTS是官方推荐的方向,但Groovy目前还是有很多项目在用,特别是旧项目。 另外,要给出实际建议,比如新项目推荐KTS,旧项目逐步迁移,以及如何开始使用KTS,比如转换build.gradle到.build.gradle.kts,使用IDE的转换工具,注意语法差异,分阶段迁移。 还要注意之前用户提到的系统级指令,确保数学公式用$...$,独立公式用$$...$$,并且用中文回答,结构清晰。需要检查是否有遗漏的要点,比如是否需要示例代码,但用户这次没有明确要求,可能不需要,但可以提到在build.gradle.kts中的基本写法。 最后,总结两者的对比,强调根据项目情况选择,并指出未来的趋势是KTS,但Groovy仍会存在一段时间。确保回答真实可靠,参考官方文档和社区实践。</think>以下是针对 **Android Studio 中使用 KTS(Kotlin DSL)还是 Groovy** 的详细对比与建议: --- ### 一、**核心区别** | **特性** | **Groovy (传统 DSL)** | **KTS (Kotlin DSL)** | |------------------|-----------------------------------------------|-----------------------------------------------| | **语法** | 动态类型,宽松语法(允许省略括号、分号等) | 静态类型,严格语法(需遵循 Kotlin 语法规则) | | **类型安全** | 弱类型,依赖运行时检查,易隐藏错误 | 强类型,编译时检查,减少配置错误 | | **IDE 支持** | 自动补全和导航较弱 | 强大的自动补全、类型推导和代码导航 | | **性能** | 构建速度较快(历史优化成熟) | 早期较慢,随版本优化逐渐接近 Groovy | | **可维护性** | 简单脚本易写,复杂逻辑可读性差 | 结构化更强,适合大型项目 | | **未来趋势** | 逐步被官方弱化,但仍有大量存量项目 | 官方推荐方向,新项目首选 | --- ### 二、**Groovy 的优势与劣势** #### **优势** 1. **上手简单**:语法灵活,适合快速编写简单脚本。 ```groovy android { compileSdkVersion 33 defaultConfig { minSdk 21 } } ``` 2. **成熟稳定**:长期作为 Android 默认 DSL,社区资源丰富。 3. **构建速度**:历史优化更充分,大型项目可能略快于 KTS。 #### **劣势** 1. **类型不安全**:配置错误可能延迟到运行时才发现。 2. **维护成本高**:复杂逻辑可读性差,重构困难。 3. **IDE 支持弱**:代码提示和跳转功能有限。 --- ### 三、**KTS 的优势与劣势** #### **优势** 1. **类型安全**:编译时检查配置错误,减少构建失败。 ```kotlin android { compileSdk = 33 defaultConfig { minSdk = 21 } } ``` 2. **代码可维护性**:支持函数、类等结构化编程,适合复杂逻辑。 3. **IDE 友好**:Android Studio 对 KTS 的自动补全、重构支持更完善。 4. **与 Kotlin 生态统一**:可直接调用 Kotlin 代码或库。 #### **劣势** 1. **学习曲线**:需熟悉 Kotlin 语法(尤其对 Java 开发者)。 2. **早期性能问题**:过去构建速度较慢,但 AGP 7.0+ 已显著优化。 3. **兼容性**:某些旧插件可能未适配 KTS。 --- ### 四、**实际建议** #### 1. **新项目** - **优先选择 KTS**:官方推荐方向,长期维护成本更低,尤其适合中大型项目。 - 示例:直接创建 `.gradle.kts` 文件,使用 Kotlin 语法编写配置。 #### 2. **旧项目** - **逐步迁移**:从 `settings.gradle` 和根 `build.gradle` 开始,分模块迁移。 - 工具支持:Android Studio 提供 **GroovyKTS 自动转换工具**(右键点击文件选择转换)。 #### 3. **关键注意事项** - **语法差异**: - Groovy:`compileSdkVersion 33` - KTS:`compileSdk = 33`(注意 `=` 赋值和类型推断) - 字符串处理:KTS 中需显式使用 `"${variable}"` 模板。 - **插件兼容性**:确认第三方插件是否支持 KTS(查看文档或 Issue 讨论)。 - **性能调优**:启用配置缓存(`--configuration-cache`)提升构建速度。 --- ### 五、**迁移步骤示例** 1. **重命名文件**:`build.gradle` → `build.gradle.kts`。 2. **修正语法**: ```kotlin // Groovy implementation 'com.android.support:appcompat-v7:28.0.0' // KTS implementation("com.android.support:appcompat-v7:28.0.0") // 注意括号和引号 ``` 3. **处理变量**:将 `ext` 全局变量改为 `extra.set("versionCode", 1)` 或使用 `gradle.properties`。 --- ### 六、**总结** - **未来趋势**:Google 正推动 KTS 成为 Android 生态标准,**推荐优先使用**。 - **过渡期策略**:Groovy 短期内不会消失,但新特性(如 Version Catalogs)更倾向 KTS。 - **决策依据**:根据团队技术栈(是否熟悉 Kotlin)和项目规模选择。 如果需要更具体的迁移案例或问题排查,可以进一步补充上下文! 🚀
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值