来到新公司后,小灵通开始接手了核心技术-快编插件,看到传说中的核心技术,小灵通傻眼了,啊这,groovy 写的插件,groovy 认真的嘛,2202 年了,插件咋还用 groovy 写呢,我新手写插件也换 kotlin 了,张嘴就是 这辈子都不可能写 groovy,甭想了。 但是嘛,工作不寒碜,学学呗。
一开始和组里几个大佬聊下来,磨刀霍霍准备对历史代码动刀,全迁移到android/harmonyos kotlin 上爽一发,但发现。。。咦,代码好像看不懂诶,我不知道 kt 对应kotlin语言的写法是啥样的。文章结束,小灵通因此被辞退。
开个玩笑,我现在还是在岗状态。工作还是要继续的。既然能力有限我全部迁不过去,那我可以做到新需求用gradle依赖冲突强制指定 kotlin 来写嘛,咦,这就有意思了。
Groovy 和 java 以及 kotlin 如何混编
怎么实现混编
我不会嘛,看看官方怎么写的。gradle 源码有这么段代码来阐释了是怎么优先 gr效率计算公式oovy 编译 而非 java 编译.
// tag::compile-task-classpath[]
tasks.named('compileGroovy') {
// Groovy only needs the declared dependencies
// (and not longer the output of compileJava)
classpath = sourceSets.main.compileClasspath
}
tasks.named('compileJava') {
// Java also depends on the result of Groovy compilation
// (which automatically makes it depend of compileGroovy)
classpath += files(sourceSets.main.groovy.classesDirectory)
}
// end::compile-task-classpath[]
噢,gradle文件夹可以删吗可以这么写啊,那我是不是抄下就可以了,把名字改改。我就可以写 kotlin 了,欧耶!
compileKotlin {
classpath = sourceSets.main.compileClasspath
}
compileGroovy {
classpath += files(sourceSets.main.kotlin.classesDirectory)
}
跑一发,没有意外的话,你会看到这个报错。
诶,为啥我照着抄就跑不起来呢?我怀疑是 kotlin classesDiretory 有问题,断点看一波 compileGroovy 这个 task 的 sourceSets.main.kotlin.classesDirectory 是个啥。大概长这样, 是个 DefaultDirectoryVar 类。
诶,这是个啥,一kotlin是什么开始我也看不太懂,觉得这里的 value 是 uAndroidndefined 怪怪的,也不确定,那我看看其他正常的 classesDirectory 是啥
其实到这里可以确定应该是 kotlin 的 classDirectory 在此时是不可用的状态,印证下自己猜想,尝试添加 catch 的断点,确实是这样
具体为啥此时还不可用,我没有更详细的深入了,有大佬知道的,android的drawable类可以不吝赐教下kotlin什么意思。
SO 搜了一波解答,看到一篇靠谱的回复 compile-groovy-and-kotlin.
compileGroovy.dependsOn compileKotlin compileGroovy.classpath += files(compileKotlin.destinationDir)
试了一下确实是可以的,但为啥这样可以了呢?以及android是什么系统最上面官方的代码是啥意思呢?还有一些奇奇怪怪的名词是啥,下面吹一下
关于 souceset
我们入门写 android 时,都看到 / 写过类似这样的代码
sourceSets {
main.java.srcDirs = ['src/java']
}
我对他的理解是指接口卡定 main sourceset 下的 java 的源码目录。 SourceSets 是一个 Sourset 的容器用来创建一个个效率的 SourceSet, 比如 main, test. 而 main 下的 java, groovy, kotlin 目录是一个编译目录(SourceDirectorySet),编译实质是找到一个个的编译目录,然后将他们变成 .class 文件放在 build/classes/sourceDirectorySet
下面, 也就是 destinationDirectory。
像 main 对应的是 SourceSet 接口,其实现是 DefaultSourceSet。而 main 下面的 groovy, java, kotlin 是 SourceDirectorySet 接口,其实现是 DefaultS效率公式ourceDirectorySet。
官方接口自动化 gradle 对于 sgradle是什么ourceset 的定义是:
- the source files and where they’re located 定位源码的位置
- the compilation classpath, including any required dependencandroid的drawable类ies (via Gradle configurations) 编译时kotlin怎么读的 class path
- where the compandroid是什么手机牌子iled clasandroid平板电脑价格s files are placed 编译出的 class 放在哪
输入文件 + 编效率译时 classpath 经过 AbstractCoandroid的drawable类mpile Task 得到 输出的 class 目录
第二个 编译时的 classpath,在项目里也见过,sourceSetImplementation 声明 sourceSet 的依赖。第三个我很少见到,印象不深,SourceDirectorySet#destinat接口crc错误计数ionDirectory 用来指定 compile task 的效率的拼音输出目录。而 SourceDirectorySet#classeskotlin为什么流行不起来Directory 和这个值是一致的。再重申一遍这里的 SourceDireckotlin是什么torySet 想成是 DSL 里写的 java,kotlin为什么流行不起来 groovy,kt 就好了。
官方文档对于 class效率意识方面存在的问题esDirectory 的描述是
The directory propegradle是什么rty that is bound to the tasandroid/harmonyosk that produces the output vi接口crc错误计数a
SourceDirectorySet.compiledBy(org.gradle.api.tasks.TaskProvider, java.util.function.Function)
. Use this as part of a classpath or input to another task to eandroid平板电脑价格nsure that the output is created before it iandroid平板电脑价格s used. Nandroidstudio安装教程ot接口crc错误计数e: To define the path of the output folder ukotlin什么意思seSourceDirectorySet.getDestinationDirectory()
大意是android是什么系统 classesDirectory 与这个 compile task 的输出是相关联的,具体是通过 SourceDirectorySet.compiledBy() 方法,这个字段由gradle项目 destinationDirectory 字段决定。查看 Defa接口测试ultSourceDirectorySet#compiledBy 方法
public <T extends Task> void compiledBy(TaskProvider<T> taskProvider, Function<T, DirectoryProperty> mapping) {
this.compileTaskProvider = taskProvider;
taskProvider.configure(task -> {
if (taskProvider == this.compileTaskProvider) {
mapping.apply(task).set(destinationDirectory);
}
});
classesDirectory.set(taskProvider.flatMap(mapping::apply));
}
雀食语义上 classesDirectory ==androidstudio安装教程 destinationDirectory。
现在我们可以去理解下 官方的接口crc错误计数 demo 了,官方的 demo 简gradle文件夹可以删吗单说就是优先执行 C效率ompile Gandroid手机roovy task, 再去执行 Compile Java taskgradle教程.
tasks.named('compileGroovy') {
classpath = sourceSets.main.compileClasspath // 1
}
tasks.named('compileJava') {
classpath += files(sourceSets.main.groovy.classesDirectory) // 2
}
可能看不懂的地方是 1,2 注释处做了啥, 1 处我问了我们组大佬,这是重置了 compileGr接口测试用例设计oovy task 的 classpath 使其不依赖 compile java cl接口测试asspath,在 Grandroid是什么系统oovyPlugin 源码中有那么一句代码
classpath.from((Callable<Object>) () -> sourceSet.getCompileClasspath().plus(target.files(sourceSet.getJava().getClassesDirectory())));
可以看到 GroovyPlugin 其实是依赖于 java 的 clas接口是什么spath 的。gradle项目这里我们需要改变 groovy 和 java 的编译android平板电脑价格时序需要把效率意识方面存在的问题这层依赖断开。
2呢,使 compileJava 依赖上 compileGrkotlin和javaoovy 的 output property,间接使 compileJava dependson compileGroovy 任务。
具体为啥 Kotlin 的不行,android什么意思俺还没搞清楚,知道的大佬可以指教下。
而 SO 上的这个答效率公式复其实也是类似的,而且更直接
compileGroovy.dependsOn compileKotlin
compileGroovy.classpath += files(compileKotlin.destinationDir)
使 compileGroovy 依赖于 compileKotlin 任务,再让 compileGroovy 的 classPath 添加上 compileKotlin 的 output. 既然任务的 classPath 添加 另一个效率集任务的 output 会自动依赖上另一个 task。那其实这么写也是可以的
compileGroovy.classpath += files(compileKotlin.destinationDir)
实验了下雀食是可以跑的. 那既然 Groovy 和 Java 都包含 maAndroidin 的 classpath,是不是 compileKotlin 的效率计算公式 classpath 置为 main,那 compileGroovy 会自动依赖上 compileKotlin。试试呗
compileKotlin.classpath = sourceSets.main.compileClasspath
可以看到 kotlin 的执行顺序雀食跑到了最前面。kotlin怎么读
在项目实操中,我发现 Kotlin 跑在了 compile 的最前面,那其实 kotlin 的类里面是不能依赖 java 或者 groovy 的任何依赖的。这也符合预期,不然就gradle依赖冲突强制指定会出现依赖成环,报 Circular dependsOn hierarchy found in the Kotlin source sets 错误。我个人观点这是一种对历史代码改造的折衷,在新需求上使用 kotlandroid的drawable类in 进行开发,一些功能相同androidstudio安装教程的工具类能翻译成 kt 就翻译,不能就重写一套。
小结
- 在这节讲了两种实现混编的方案。写法不同,本质都是使一个任务依赖另androidstudio安装教程一个任务的 output
// 1
compileGroovy.classpath += files(compileKotlin.destinationDir)
// 2
compileKotlin.classpath = sourceSets.main.compileClasspath
- 我对于 SourceSet 和 SourceDirectogradle是什么rySet 的理解
- 项目中实践混编方案的现状
Groovy 有趣的语法糖
在写kotlin匿名函数 Groovy 的过程中,我遇到一个头大的问题,代码看不懂,里面有一些奇奇怪怪没见过的语法糖,乍一看就懵了,你要不一起瞅瞅。
includes*.tasks
我司的仓库是大仓的结构,仓库和子仓之间是通过 Composite build 构建联系接口自动化的。那么怎么使主仓的 task 触发 includeBuild 的仓库执行对应仓库呢?是通过这行代码实现的
tasks.register('publishDeps') {
dependsOn gradle.includedBuilds*.task(':publishIvyPublicationToIvyRepository')
}
这里的 includeBuilds*.task 后面的 *.task 是啥?includegradle是什么Builds 看源码发现是个 List。我不懂 groovy,但好歹我能gradle和maven看懂 kotli效率公式n, 我kotlin和java看看官方文档右边对应的 kt 写法是啥?
tasks.register("publishDeps") {
dependsOn(gradle.includedBuilds.map { it.task(":publishMavenPublicationToMavenRepository") })
}
咦嘿,原来是个 List 的 map 操作,骚里骚气的。翻了翻原来是个 gandroid手机roovy 的语法糖,写个代码试试看看他编译到 class 是啥样子
def list = ["1", "22", "333"]
def lengths = list*.size()
lengths.forEach{
println it
}
编译成 class
Object list = ScriptBytecodeAdapter.createList(new Object[]{"1", "22", "333"});
Object lengths = ScriptBytecodeAdapter.invokeMethod0SpreadSafe(Groovy.class, list, (String)"size");
var1[0].call(lengths, new Groovy._closure1(this, this));
在 ScriptBytecandroid什么意思ode接口Adapter.invokeMethod0SpreadSafe 实现内部其实还是新建了一个 List 再逐个对 List 中元素进行 map.
String.ekotlin什么意思xecute
这是执行一个 shell 指令,比如 “landroid下载s -al”.execute(), 刚看到这个的时候认为这个接口文档东西类似 kotlin 的扩展函数,点进去看实现发现不一样
public static Process execute(final String self) throws IOException {
return Runtime.getRuntime().exec(self);
}
可以看到 receiver 是他的第一个参数,莫非android是什么手机牌子这是通用的语法糖,我试试写了个
public static String deco(final String self) throws IOException {
return self + "deco"
}
// println "".deco()
运行下,哦吼,跑不了,报了 MissingMethodException。看样子是不通用的。翻了翻 groovy 文档,找到了这个文档
Static methods are used with the first parameterandroid什么意思 being thekotlin语言 destination class, i.e.
public static String reverse(String self)
provides areverse()
method forString
.
看样子这个语法糖是 groovy 内部android什么意思定制的,我不清楚有没有支持开发定制的方式,效率是什么意思知道的大佬可以评论区留言下。
Range 怎么写
groovy 也有类似 kotlin 的接口crc错误计数 Range 的概念,包含的 Range 是 ..
, 不包含右边界(kotlin是什么until)的是 ..<
Try with resources
我遇到过一个 OKHttp 连接泄露的问题,代码原型大概是这样
if (xxx) {
response.close()
} else {
// behavior
}
定位到是 Response 没有在 else 的分支上进行 close,当然可以效率集简单在 else 分支上进行 close, 并在外层补上 try, catch 兜底,但在 Effective Java
一kotlin语言书提及针对资源关闭 try-with-resou效率计算公式rce 优于 try cactch。但我尝试像 java 一样写 try-with-resource,发现嗝屁了,直接报红,我去 SO 上搜了一波 groovy 的 try-with-resource. Groovy 是通过 withCloseable 扩展来实现,看这个方法的声明与 Prockotlin和java区别ess效率的英文#execute 语法糖类似—public static def withCloseable(Closeable self, Clkotlin是什么osure action) . 最终改造后的代码是这样的
Response.withCloseable { reponse ->
if (xxx) {
} else {
}
}
<<
这个是 groovy 中的左移运算符效率也是可以重载的,而 kotlin 是不支持的。他运用比较多的场景。起初我印象中 Task 的 是覆写了这个运算符作为gradle怎么读 doLast 简易写法,现在 gradle7.效率集X 的版本上是没有了。其它常见的是文件写入操作, 列表添加元素。
def file = new File("xxx")
file << "text"
def list = []
list << "aaa"
Groovy 的一家之言
如果 kotlinandroid是什么系统 是 better java, 那么 groovy 应该是 more than java,它的定接口文档位更加偏向脚本一些,更加Kotlin动态化(从它反编译的字节码可见一斑),上手曲线较高,但一个人精通这个语言,并且独立维护一个项目,其实 groov接口英文y 的开发效率并不会比gradle菜鸟教程 kotlkotlin是什么in 和 java 差,感受比较深切的是 maven publish 的例子,看看插件中 groovy 和 kotlin效率高发票查验 的写法上的不同。
// Groovy
def mavenSettings = {
groupId 'org.gradle.sample'
artifactId 'library'
version '1.1'
}
def repSettings = {
repositories {
maven {
url = mavenUrl
}
}
}
afterEvaluate {
publishing {
publications {
maven(MavenPublication) {
ConfigureUtil.configure(mavenSettings, it)
from components.java
}
}
ConfigureUtil.configure(repoSettings, it)
}
def publication = publishing.publications.'maven' as MavenPublication
publication.pom.withXml {
// inject msg
}
}
// Kotlin
// Codes are borrowed from (sonatype-publish-plugin)[https://github.com/johnsonlee/sonatype-publish-plugin/]
fun Project.publishing(
config: PublishingExtension.() -> Unit
) = extensions.configure(PublishingExtension::class.java, config)
val Project.publishing: PublishingExtension
get() = extensions.getByType(PublishingExtension::class.java)
val mavenClosure = closureOf<MavenPublication> {
groupId = "org.gradle.sample"
artifactId = "library"
version = "1.1"
}
val repClosure = closureOf<PublishingExtension> {
repositories {
maven {
url = mavenUrl
}
}
}
afterEvaluate {
publishing {
publications {
create<MavenPublication>("maven") {
ConfigureUtil.configure(mavenClosure, this)
from(components["java"])
}
}
ConfigureUtil.configure(repoClosure, this)
}
val publication = publishing.publications["maven"] as MavenPublication
publication.pom.withXml {
// inject msg
}
}
我觉得吧,kotlin是什么如果像我们大佬擅长 groovy 的话,而且是一个人开发的商业项目,插件里gradle下载的确写 groovy 会更快,更简洁,那为什么不呢?这对他来说是种善,语言没有优劣,动态性和静态语言优劣我不想较高下,这因人而异。 我选择 kotlin 是俺不擅长写 groovy 啊,我写了几个月 groovy 每次改动插件发布后再应用第一次都会有语法错误,调试的头皮发麻,所以最后搞了个折衷方案,新代码用 kotlin, 旧代码用 groovy 继续写。而且参考了 KOGE@2BAB 文档,发现咦,gradle 正面回应过 groovy 与 kotlin 之争. “Prefer using a statically-typed language to implement a plugin”@Gradle。嗯, 我还是继续写 Kot效率高发票查验lin 吧kotlin是什么。