简介
本文假设各位已经有了 Kotlin 基础,对 Kotlin 还不熟悉的小伙伴可以去看我之前发的文章-->《Kotlin Jetpack 实战》。
本文将带领各位一步步将 Demo 工程 的 Gradle 脚本改成 Kotlin DSL,让我们一起实战吧!
正文
1. Kotlin 编写 Gradle 脚本的优势
Kotlin | Groovy | |
自动代码补全 | 支持 | 不支持 |
是否类型安全 | 是 | 不是 |
源码导航 | 支持 | 不支持 |
重构 | 自动关联 | 手动修改 |
2. 实战前的准备
- 将 Android Studio 版本升级到最新
- 将我们的 Demo 工程 clone 到本地,用 Android Studio 打开:github.com/chaxiu/Kotl…
- 切换到分支:
chapter_02_kotlin_dsl_training
- 强烈建议读者跟着本文一起实战,实战才是本文的精髓。
3. 开始重构
3-1. 将单引号
替换成双引号
替换前:
替换后:
小结:
- 不用修改 Gradle 文件扩展名,直接使用 Android Studio 替换功能即可。
- 为什么能够直接替换?因为 Grooovy 和 Kotlin 在字符串定义的语法是相近的:
双引号
表示字符串。 - 那么,为什么要替换呢?因为
单引号
双引号
在 Groovy 里都是定义字符串,而 Kotlin 里单引号
定义的是单个字符
,双引号
才是定义字符串。
具体细节可以看我这个 GitHub Commit
3-2. 修改 Gradle 文件扩展名
- builde.gradle --> build.gradle.
kts
- settings.gradle --> settings.gradle.
kts
- Sync 走起!
Script compilation errors: Line 1: include ":app" Unexpected tokens (use ';' > to separate expressions on the same line) Line 1: include ":app" Function invocation 'include(...)' > expected 2 errors
不要慌! 报错不可怕,不报错才可怕!最起码我们知道哪里错了。 错误日志告诉我们,问题出在这里:
我们 Command + 鼠标左键
点击 include,来看看源码实现:
哟!原来 settings.gradle 里面的 include 的本质就是个方法调用
啊!再结合报错原因:Function invocation 'include(...)' > expected
,这就单纯是个语法错误呗!就是说,我们改了 Gradle 扩展名以后,IDE 就认为它是个 Kotlin 语句了。而 include ":app"
用的还是 Groovy 的语法,这当然会报错了!
修改成这样就好了:
接下来重复这个的步骤:Sync --> 报错 --> Command + 鼠标左键
看源码
修改前:
修改后:
3-3. 遇到无法解决的报错怎么办?
比如:如果你继续 Sync,报错的是这里:
e: /KotlinJetpackInAction/build.gradle.kts:19:16: Expecting ')' e: ../KotlinJetpackInAction/build.gradle.kts:19:16: Unexpected tokens (use ';' to separate expressions on the same line) e: ../KotlinJetpackInAction/build.gradle.kts:20:23: Expecting an element e: ../KotlinJetpackInAction/build.gradle.kts:20:32: Expecting an element e: ../KotlinJetpackInAction/build.gradle.kts:19:1: Function invocation 'task(...)' expected e: ../KotlinJetpackInAction/build.gradle.kts:19:1: None of the following functions can be called with the arguments supplied: public abstract fun task(p0: String!): Task! defined in org.gradle.api.Project public abstract fun task(p0: String!, p1: Closure<(raw) Any!>!): Task! defined in org.gradle.api.Project public abstract fun task(p0: String!, p1: Action<in Task!>!): Task! defined in org.gradle.api.Project public abstract fun task(p0: (Mutable)Map<String!, *>!, p1: String!): Task! defined in org.gradle.api.Project public abstract fun task(p0: (Mutable)Map<String!, >!, p1: String!, p2: Closure<(raw) Any!>!): Task! defined in org.gradle.api.Project e: ../KotlinJetpackInAction/build.gradle.kts:19:12: Function invocation 'type(...)' expected e: ../KotlinJetpackInAction/build.gradle.kts:19:12: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: public inline fun ObjectConfigurationAction.type(pluginClass: KClass<>): ObjectConfigurationAction defined in org.gradle.kotlin.dsl e: ../KotlinJetpackInAction/build.gradle.kts:20:5: Function invocation 'delete(...)' expected e: ../KotlinJetpackInAction/build.gradle.kts:20:12: Unresolved reference: rootProject
好可怕,这回一次性报好多错误,而且看起来都很奇怪。怎么办? 不要慌! Kotlin 官方都已经给我们准备好了迁移指南: Migrating build logic from Groovy to Kotlin
嫌上面都迁移指南太长?还是纯英文? 不要怕! Kotlin 官方给我们准备了迁移案例: kotlin-dsl-samples:hello-android
看!迁移案例里已经告诉我们怎么改这个 clean task 了:
3-4. 参照kotlin-dsl-samples继续修改
修改前:
修改后:
修改前:
修改后:
修改前:
修改后:
具体可以看我这个 Github Commit
3-5 大功告成!
这下我们可以开始愉快的用 Kotlin 写 Gradle 脚本了。
那么,这篇文章是不是就该结束了呢?并没有。
本文是以实战
为核心,咱们刚用 Kotlin DSL 重构完项目,当然还要再实战一波啦!
4. Kotlin DSL 实战--依赖管理
4-1. Groovy 时代的依赖管理
以前我们这么定义依赖:
然后这么用:
4-2. Kotlin 时代的依赖管理
Kotlin DSL 管理依赖的方式很多,我这里采用相对主流的做法:buildSrc
目录下管理。具体细节可以看这个官方文档:Gradle Documentation
简单翻译:
Gradle 运行的时候,会去检查工程根目录下是否存在
buildSrc
目录,如果存在,这个目录下的所有脚本都会自动被添加到工程的环境变量(classpath)里。
借助上面的机制,我们就可以把所有依赖的都以常量形式定义到 buildSrc
目录下,然后我们就可以直接在工程里随意使用了。
具体结构如下所示:
ProjectProperties.kt 定义了工程相关的属性:
Libs.kt 定义了所有的依赖:
Versions.kt 定义了所有依赖库的版本号:
对应 build.gradle.kts 的修改如下:
具体细节可以看这个 Github Commit:
看,现在我们 Gradle 代码就能有自动补全的提示了。
真香!
结尾
注意:
在新的工程里用 Kotlin DSL 完全替代 Groovy 是一件很简单的事情,但如果是一个年代久远的工程那就没那么容易了,大坑小坑会不少
,Kotlin DSL 迁移的坑后面会讲。
下一节,我会一步步把 Demo 工程里的 Java 代码重构成 Kotlin。