前言: Android gradle 的 Groovy 转 Kotlin 的难点在于groovy非常不规范,以为是一个变量其实是一个方法,以为是方法其实是一个一个字符串,koltin提供了更严格的语法。改为kotlin之后最大的变化就是扩展字段了,和groovy的语法完全不一样,需要好好了解学习一下,本文也会讲到。
下面是我对自己的项目改造总结,使用的gradle版本是gradle-7.3.3,agp版本是7.2.1。
文件重命名
改造前首先要将原来的gradle文件加上‘kts’后缀,如build.gradle改为build.gradle.kts,kts是Kotlin Script的缩写。重命名后AndroidStudio会自动识别为kotlin dsl。
项目的build.gradle
-
groovy
// Top-level build file where you can add configuration options common to all sub-projects/modules. apply from: 'config.gradle' apply plugin: 'com.alibaba.arouter' buildscript { repositories { maven { url "https://maven.aliyun.com/repository/public" } } dependencies { classpath 'com.android.tools.build:gradle:7.2.1' classpath "com.alibaba:arouter-register:1.0.2" } } allprojects { repositories { maven { url "https://maven.aliyun.com/repository/public" } } configurations.all { // fix 依赖冲突 resolutionStrategy.force "androidx.appcompat:appcompat:1.4.2" resolutionStrategy.force "androidx.recyclerview:recyclerview:1.2.1" } } task clean(type: Delete) { delete rootProject.buildDir }
-
kotlin
apply(from = "config.gradle.kts") apply(plugin = "com.alibaba.arouter") buildscript { repositories { maven("https://maven.aliyun.com/repository/public") } dependencies { classpath(libs.android.gradlePlugin) classpath(libs.greendao.gradlePlugin) } } allprojects { repositories { maven("https://maven.aliyun.com/repository/public") } configurations.all { resolutionStrategy.force("androidx.appcompat:appcompat:1.4.2") resolutionStrategy.force("androidx.recyclerview:recyclerview:1.2.1") } } tasks.create<Delete>("clean") { delete(rootProject.buildDir) }
module的build.gradle
-
groovy
apply plugin: 'com.android.application' apply plugin: 'org.greenrobot.greendao' android { compileSdk = libs.versions.compileSdk.get().toInteger() ndkVersion = libs.versions.ndkVersion.get() defaultConfig { applicationId "com.myapp.sample" minSdkVersion(libs.versions.minSdk.get().toInteger()) targetSdkVersion(libs.versions.targetSdk.get().toInteger()) versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true externalNativeBuild { cmake { cppFlags "" abiFilters "arm64-v8a", "armeabi-v7a", "x86" } } } signingConfigs { app { keyAlias 'appkey' keyPassword '******' storeFile file('key.jks') storePassword '******' } } buildTypes { release { minifyEnabled true zipAlignEnabled true signingConfig signingConfigs.app proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', 'proguard-android.pro', 'proguard-third-libs.pro' } debug { minifyEnabled false debuggable true signingConfig signingConfigs.app } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } buildFeatures { dataBinding true } greendao { schemaVersion 1 } externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.10.2" } } lintOptions { abortOnError false } } dependencies { configurations.all { // check for updates every build resolutionStrategy.cacheChangingModulesFor 30, 'seconds' } implementation(libs.glide) annotationProcessor(libs.glide.compiler) implementation(libs.androidx.constraintlayout) implementation(project(':easyPhotos')) implementation(libs.google.zxing.core) { exclude group: 'com.google.guava' } }
-
kotlin
plugins { id("com.android.application") id("org.greenrobot.greendao") } android { compileSdk = libs.versions.compileSdk.get().toInt() ndkVersion = libs.versions.ndkVersion.get() defaultConfig { applicationId = "com.myapp.sample" minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled = true externalNativeBuild { cmake { cppFlags += listOf() abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86") } } } signingConfigs { create("release") { keyAlias = "appkey" keyPassword = "******" storeFile = file("key.jks") storePassword = "******" } } buildTypes { release { isMinifyEnabled = true isZipAlignEnabled = true signingConfig = signingConfigs.getByName("release") proguardFiles( getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro", "proguard-android.pro", "proguard-third-libs.pro" ) } debug { isMinifyEnabled = false isDebuggable = true signingConfig = signingConfigs.getByName("release") } } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } buildFeatures { dataBinding = true } greendao { schemaVersion = 1 } externalNativeBuild { cmake { path("src/main/cpp/CMakeLists.txt") version = "3.10.2" } } lintOptions { isAbortOnError = false } } dependencies { configurations.all { // check for updates every build resolutionStrategy.cacheChangingModulesFor(30, TimeUnit.SECONDS) } implementation(libs.glide) annotationProcessor(libs.glide.compiler) implementation(project(":easyPhotos")) implementation(libs.androidx.constraintlayout) implementation(libs.google.zxing.core) { //也可这么写 exclude(group = "com.google.guava") exclude(mapOf("group" to "com.google.guava")) } }
关于扩展字段
一、首先是新增扩展字段
groovy要新增扩展字段只需在ext{}
里面随便加字段,kotlin改为用extra
,语法也做了改变,具体看下面例子:
-
groovy
//这里在项目根目录的user.gradle下新增用户名和密码 ext { username = 'JohnSnow' password = 'abcxxx' }
-
kotlin 有三种写法
// 写法一,如果不写rootProject默认会加上当前的project rootProject.extra.apply { set("username", "JohnSnow") set("password", "abcxxx") } // 写法二 val username : String by rootProject.extra("JohnSnow") val password : String by rootProject.extra("abcxxx") // 写法三,大括号里面可以写语句,最后一行是返回值 val username : String by rootProject.extra{ val name = "JohnSnow" name } val password : String by rootProject.extra{"abcxxx"}
二、使用扩展字段
-
groovy
def name = rootProject.ext.username def pwd = rootProject.ext.password
-
kotlin 有两种使用方式
//方式一,这种方式就是Map的key-value形式 val name = rootProject.extra["username"] as String val pwd = rootProject.extra["password"] as String //方式二,这是kotlin dsl提供的语法糖,这种方式指定的变量名username必须与extra里的"username"一致, //且必须指定字段类型(String) val username: String by rootProject.extra val password: String by rootProject.extra
关于上传到maven的写法区别
上传到maven有两种写法,
- 一种是直接在当前要上传的library模块的build.gradle里写上传脚步代码
这种方式自带模块的project环境,kotlin dsl做了一些适配,写法会更简单些
- 第二种是放到一个单独的maven.gradle里统一处理,这种方式适用于有很多个library需要上传的情况,不用每个模块都写一遍上传脚本;
这种方式是在单独的gradle里编写没有自带project的环境,所以第二种方式的写法会复杂一些,而且没有代码提示。
一、在library模块编写
-
groovy
apply plugin: 'com.android.library' android { ...略 //提交到顺丰maven所需的配置 publishing { singleVariant('release') { withSourcesJar() } } } // maven插件 apply plugin: 'maven-publish' afterEvaluate { def GROUP_ID = "com.free.sdk" def ARTIFACT_ID = "wo-util" def VERSION = "1.0.0" publishing { publications { //mylib名字随意 mylib(MavenPublication) { from components.release groupId = GROUP_ID artifactId = ARTIFACT_ID version = VERSION pom.withXml { println "!!!!>>>>>> ${project.name} 要上传的版本-> $groupId:$artifactId:$version" println "!!!!>>>>>> ${project.name} 依赖的包:" configurations.implementation.allDependencies.each { if (it.version != "unspecified") { // 过滤项目内library引用 println " " + it.group + ":" + it.name + ":" + it.version } } } } } //需要发布的目标仓库 repositories { maven { url = rootProject.extra["mavenUrl"] as String credentials {//密码认证 username = rootProject.extra["username"] as String password = rootProject.extra["password"] as String } } } } }
-
kotlin
plugins { id("com.android.library") //注意maven插件要写在头部 `maven-publish` } android { ...略 //提交到顺丰maven所需的配置 publishing { singleVariant("release") { withSourcesJar() } } } afterEvaluate { val GROUP_ID = "com.free.sdk" val ARTIFACT_ID = "wo-util" val VERSION = "1.0.0" publishing { publications { //mylib名字随意 register<MavenPublication>("mylib") { from(components["release"]) groupId = GROUP_ID artifactId = ARTIFACT_ID version = VERSION pom.withXml { println("!!!!>>>>>> ${project.name} 要上传的版本-> $groupId:$artifactId:$version") println("!!!!>>>>>> ${project.name} 依赖的包:") configurations.implementation.allDependencies.forEach { dependency -> if (dependency.version != "unspecified") { println(" ${dependency.group}:${dependency.name}:${dependency.version}") } } } } } repositories { maven { setUrl(rootProject.extra["mavenUrl"] as String) credentials { username = rootProject.extra["username"] as String password = rootProject.extra["password"] as String } } } } }
二、在maven.gradle里统一处理
-
在library的build.gradle.kts里引入maven.gradle
plugins { id("com.android.library") } apply(from = "${rootProject.rootDir}/maven.gradle.kts")
-
maven.gradle.kts里的代码
import org.gradle.api.publish.PublishingExtension apply(plugin = "maven-publish") val libraries: Map<String, Map<String, String>> by rootProject.extra val library = libraries[project.name]!! val sonatype: Map<String, String> by rootProject.extra val repositoryUrl = if ((library["version"] as String).endsWith("SNAPSHOT")) { sonatype["snapshotUrl"]!! } else { sonatype["releaseUrl"]!! } //publishing报错, 改为 configure<PublishingExtension>原因: //https://stackoverflow.com/questions/54654376/why-is-publishing-function-not-being-found-in-my-custom-gradle-kts-file-within configure<PublishingExtension> {// --------不同点1️⃣ publications { register<MavenPublication>("mylib") { afterEvaluate { from(components["release"]) } groupId = rootProject.extra["groupId"] as String artifactId = library["artifactId"] version = library["version"] pom.withXml { println("!!!!>>>>>> ${project.name} 要上传的版本-> $groupId:$artifactId:$version") println("!!!!>>>>>> ${project.name} 依赖的包:") configurations { "implementation" {// --------不同点2️⃣ allDependencies.forEach { if (it.version != "unspecified") { // 过滤项目内library引用 println(" " + it.group + ":" + it.name + ":" + it.version) } } } } } } } repositories { maven { setUrl(repositoryUrl) credentials { username = sonatype["username"] password = sonatype["password"] } } } }