將構(gòu)建配置從 Groovy 遷移到 KTS

將構(gòu)建配置從 Groovy 遷移到 KTS

icon.jpg

前言

作為Android開發(fā)習(xí)慣了面向?qū)ο缶幊桃怀溃?xí)慣了IDEA提供的各種輔助開發(fā)快捷功能靴拱。

那么帶有陌生的常規(guī)語法的Groovy腳本對于我來說一向敬而遠(yuǎn)之悬蔽。

Kotlin DSL的出現(xiàn)感覺是為了我們量身定做的,因?yàn)椴捎?Kotlin 編寫的代碼可讀性更高,并且 Kotlin 提供了更好的編譯時(shí)檢查和 IDE 支持。

名詞概念解釋

  • Gradle: 自動化構(gòu)建工具. 平行產(chǎn)品: Maven.

  • Groovy: 語言, 編譯后變?yōu)?code>JVM byte code, 兼容Java平臺.

  • DSL: Domain Specific Language, 領(lǐng)域特定語言.

  • Groovy DSL: Gradle的API是Java的,Groovy DSL是在其之上的腳本語言. Groovy DS腳本文件后綴: .gradle.

  • KTS:是指 Kotlin 腳本比原,這是 Gradle 在構(gòu)建配置文件中使用的一種 Kotlin 語言形式。Kotlin 腳本是可從命令行運(yùn)行的 Kotlin 代碼杠巡。

  • Kotlin DSL:主要是指 Android Gradle 插件 Kotlin DSL量窘,有時(shí)也指底層 Gradle Kotlin DSL

在討論從 Groovy 遷移時(shí)氢拥,術(shù)語“KTS”和“Kotlin DSL”可以互換使用蚌铜。換句話說,“將 Android 項(xiàng)目從 Groovy 轉(zhuǎn)換為 KTS”與“將 Android 項(xiàng)目從 Groovy 轉(zhuǎn)換為 Kotlin DSL”實(shí)際上是一個(gè)意思嫩海。

Groovy和KTS對比

類型 Kotlin Groovy
自動代碼補(bǔ)全 支持 不支持
是否類型安全 不是
源碼導(dǎo)航 支持 不支持
重構(gòu) 自動關(guān)聯(lián) 手動修改

優(yōu)點(diǎn):

  • 可以使用Kotlin, 開發(fā)者可能對這個(gè)語言更熟悉更喜歡.
  • IDE支持更好, 自動補(bǔ)全提示, 重構(gòu),imports等.
  • 類型安全: Kotlin是靜態(tài)類型.
  • 不用一次性遷移完: 兩種語言的腳本可以共存, 也可以互相調(diào)用.

缺點(diǎn)和已知問題:

  • 目前冬殃,采用 KTS 的構(gòu)建速度可能比采用 Groovy 慢(自測小demo耗時(shí)增加約40%(約8s))。

  • Project Structure 編輯器不會展開在 buildSrc 文件夾中定義的用于庫名稱或版本的常量叁怪。

  • KTS 文件目前在項(xiàng)目視圖中不提供文本提示审葬。

Android構(gòu)建配置從Groovy遷移KTS

準(zhǔn)備工作

  1. Groovy 字符串可以用單引號 'string' 或雙引號 "string" 引用,而 Kotlin 需要雙引號 "string"奕谭。

  2. Groovy 允許在調(diào)用函數(shù)時(shí)省略括號涣觉,而 Kotlin 總是需要括號。

  3. Gradle Groovy DSL 允許在分配屬性時(shí)省略 = 賦值運(yùn)算符血柳,而 Kotlin 始終需要賦值運(yùn)算符官册。

所以在KTS中需要統(tǒng)一做到:

  • 使用雙引號統(tǒng)一引號.
groovy-kts-diff1.png
  • 消除函數(shù)調(diào)用和屬性賦值的歧義(分別使用括號和賦值運(yùn)算符)。
groovy-kts-diff2.png

腳本文件名

Groovy DSL 腳本文件使用 .gradle 文件擴(kuò)展名难捌。

Kotlin DSL 腳本文件使用 .gradle.kts 文件擴(kuò)展名攀隔。

一次遷移一個(gè)文件

由于您可以在項(xiàng)目中結(jié)合使用 Groovy build 文件和 KTS build 文件皂贩,因此將項(xiàng)目轉(zhuǎn)換為 KTS 的一個(gè)簡單方法是先選擇一個(gè)簡單的 build 文件(例如 settings.gradle),將其重命名為 settings.gradle.kts昆汹,然后將其內(nèi)容轉(zhuǎn)換為 KTS明刷。之后,確保您的項(xiàng)目在遷移每個(gè) build 文件之后仍然可以編譯满粗。

自定義Task

由于Koltin 是靜態(tài)類型語言辈末,Groovy是動態(tài)語言,前者是類型安全的映皆,他們的性質(zhì)區(qū)別很明顯的體現(xiàn)在了 task 的創(chuàng)建和配置上挤聘。詳情可以參考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 將能夠獲知相關(guān)上下文信息捅彻,即使在構(gòu)建失敗時(shí)也是如此组去。IDE 可使用這些信息執(zhí)行代碼補(bǔ)全并提供其他實(shí)用建議,從而幫助您解決 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 中變?yōu)橐韵麓a:

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

如需詳細(xì)了解 plugins 代碼塊缭裆,請參閱 Gradle 的遷移指南键闺。

注意plugins 代碼塊僅解析 Gradle 插件門戶中提供的插件或使用 pluginManagement 代碼塊指定的自定義存儲庫中提供的插件。如果插件來自插件門戶中不存在的 buildScript 依賴項(xiàng)澈驼,那么這些插件在 Kotlin 中就必須使用 apply 才能應(yīng)用辛燥。例如:

apply(plugin = "kotlin-android")
apply {
    from("${rootDir.path}/config.gradle")
    from("${rootDir.path}/version.gradle.kts")
}

如需了解詳情,請參閱 Gradle 文檔缝其。

強(qiáng)烈建議您plugins {}優(yōu)先使用塊而不是apply()函數(shù)挎塌。

有兩個(gè)關(guān)鍵的最佳實(shí)踐可以更輕松地在 Kotlin DSL 的靜態(tài)上下文中工作:

  • 使用plugins {}
  • 將本地構(gòu)建邏輯放在構(gòu)建的buildSrc目錄中

plugins {}塊是關(guān)于保持您的構(gòu)建腳本聲明性,以便充分利用Kotlin DSL内边。

使用buildSrc項(xiàng)目是關(guān)于將您的構(gòu)建邏輯組織成共享的本地插件和約定榴都,這些插件和約定易于測試并提供良好的 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")

構(gòu)建變體

顯式和隱式 buildTypes

在 Kotlin DSL 中假残,某些 buildTypes(如 debugrelease,)是隱式提供的缭贡。但是,其他 buildTypes 則必須手動創(chuàng)建辉懒。

例如阳惹,在 Groovy 中,您可能有 debug眶俩、releasestaging buildTypes

buildTypes
  debug {
    ...
  }
  release {
    ...
  }
  staging {
    ...
  }

在 KTS 中莹汤,僅 debugrelease buildTypes 是隱式提供的,而 staging 則必須由您手動創(chuàng)建:

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

我們通常會把簽名信息颠印、版本信息等配置寫在gradle.properties中纲岭,在kotlin-dsl中我們可以通過一下方式訪問:

  1. rootProject.extra.properties
  2. project.extra.properties
  3. rootProject.properties
  4. properties
  5. System.getProperties()

System.getProperties()使用的限制比較多

  • 參數(shù)名必須按照systemProp.xxx格式(例如:systemProp.kotlinVersion=1.3.72);
  • 與當(dāng)前執(zhí)行的task有關(guān)(> Configure project :buildSrc> Configure project :的結(jié)果不同抹竹,后者無法獲取的gradle.properties中的數(shù)據(jù));

local.properties

獲取工程的local.properties文件

gradleLocalProperties(rootDir)

gradleLocalProperties(projectDir)

獲取系統(tǒng)環(huán)境變量的值

val JAVA_HOME:String = System.getenv("JAVA_HOME") ?: "default_value"

關(guān)于Ext

Google 官方推薦的一個(gè) Gradle 配置最佳實(shí)踐是在項(xiàng)目最外層 build.gradle 文件的ext代碼塊中定義項(xiàng)目范圍的屬性,然后在所有模塊間共享這些屬性止潮,比如我們通常會這樣存放依賴的版本號窃判。

// build.gradle

ext {
    compileSdkVersion = 28
    buildToolsVersion = "28.0.3"
    supportLibVersion = "28.0.0"
    ...
}

但是由于缺乏IDE的輔助(跳轉(zhuǎn)查看、全局重構(gòu)等都不支持)喇闸,實(shí)際使用體驗(yàn)欠佳袄琳。

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數(shù)據(jù)是可以在build.gradle.kts中使用extra進(jìn)行訪問的。

修改生成apk名稱和BuildConfig中添加apk支持的cpu架構(gòu)

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架構(gòu)信息
            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"
            }
        }
    }
}

buildSrc

我們在使用Groovy語言構(gòu)建的時(shí)候燃乍,往往會抽取一個(gè)version_config.gradle來作為全局的變量控制唆樊,而ext擴(kuò)展函數(shù)則是必須要使用到的,而在我們的Gradle Kotlin DSL中刻蟹,如果想要使用全局控制逗旁,則需要建議使用buildSrc

復(fù)雜的構(gòu)建邏輯通常很適合作為自定義任務(wù)或二進(jìn)制插件進(jìn)行封裝舆瘪。自定義任務(wù)和插件實(shí)現(xiàn)不應(yīng)存在于構(gòu)建腳本中片效。buildSrc則不需要在多個(gè)獨(dú)立項(xiàng)目之間共享代碼,就可以非常方便地使用該代碼了介陶。

buildSrc被視為構(gòu)建目錄堤舒。編譯器發(fā)現(xiàn)目錄后色建,Gradle會自動編譯并測試此代碼哺呜,并將其放入構(gòu)建腳本的類路徑中。

  1. 先創(chuàng)建buildSrc目錄箕戳;
  2. 在該目錄下創(chuàng)建build.gradle.kts文件某残;
  3. 創(chuàng)建一個(gè)buildSrc/src/main/koltin目錄;
  4. 在該目錄下創(chuàng)建Dependencies.kt文件作為版本管理類陵吸;

需要注意的是buildSrcbuild.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文件執(zhí)行順序:

gradle-wrapper.properties:5.6.4

com.android.tools.build:gradle:3.2.0

  1. BuildSrc:build.gradle
  2. setting.gradle
  3. Project:build.gradle
  4. Moudle:build.gradle

gradle-wrapper.properties:6.5

com.android.tools.build:gradle:4.1.1

  1. setting.gradle
  2. BuildSrc:build.gradle
  3. Project:build.gradle
  4. Moudle:build.gradle

所以在非buildSrc目錄下的build.gradle.kts文件中我們使用Dependencies.kt需要注意其加載順序玻墅。

參考文檔

Android官網(wǎng)-將構(gòu)建配置從 Groovy 遷移到 KTS

Migrating build logic from Groovy to Kotlin

GitHub:kotlin-dsl-samples

GitHub:kotlin-dsl-samples/samples/hello-android

Kotlin DSL: Gradle scripts in Android made easy

buildSrc官方文檔

Gradle’s Kotlin DSL BuildSrc

文章到這里就全部講述完啦,若有其他需要交流的可以留言哦~壮虫!~澳厢!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市囚似,隨后出現(xiàn)的幾起案子剩拢,更是在濱河造成了極大的恐慌,老刑警劉巖饶唤,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徐伐,死亡現(xiàn)場離奇詭異,居然都是意外死亡募狂,警方通過查閱死者的電腦和手機(jī)办素,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門角雷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人性穿,你說我怎么就攤上這事勺三。” “怎么了需曾?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵檩咱,是天一觀的道長。 經(jīng)常有香客問我胯舷,道長刻蚯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任桑嘶,我火速辦了婚禮炊汹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逃顶。我一直安慰自己讨便,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布以政。 她就那樣靜靜地躺著霸褒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盈蛮。 梳的紋絲不亂的頭發(fā)上废菱,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音抖誉,去河邊找鬼殊轴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛袒炉,可吹牛的內(nèi)容都是我干的旁理。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼我磁,長吁一口氣:“原來是場噩夢啊……” “哼孽文!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起夺艰,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤芋哭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后劲适,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體楷掉,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烹植。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斑鸦。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖草雕,靈堂內(nèi)的尸體忽然破棺而出巷屿,到底是詐尸還是另有隱情,我是刑警寧澤墩虹,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布嘱巾,位于F島的核電站,受9級特大地震影響诫钓,放射性物質(zhì)發(fā)生泄漏旬昭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一菌湃、第九天 我趴在偏房一處隱蔽的房頂上張望问拘。 院中可真熱鬧,春花似錦惧所、人聲如沸骤坐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纽绍。三九已至,卻和暖如春势似,著一層夾襖步出監(jiān)牢的瞬間拌夏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工叫编, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辖佣,地道東北人霹抛。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓搓逾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杯拐。 傳聞我的和親對象是個(gè)殘疾皇子霞篡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容