大众号「稀有猿诉」
Kotlin符号处理(Kotlin Symbol Processing)即KSP是能够用于开发轻量级编译器插件的一套API。是Kotlin原生的,Kotlin语法友爱的编译器插件。运用简略且易于上手,能够完成一些非常强壮的编译时代码处理功能,如代码生成和代码查看。今日就来学习一下KSP的基本原理,以及怎么运用KSP API。
留意,本文是Kotlin中较为高级的论题,适合有一定的Kotlin根底的同学,不然了解起来或许有难度,能够事先阅览前面的文章。
什么是KSP
与前文提到的注解处理器kapt类似,KSP也是一种编译时的插件,能够在编译前处理Kotlin言语的符号。KSP API能地道地处理Kotlin的源码,由于它是专门为Kotlin而设计的,能够完全的了解和辨认Kotlin的言语符号,以及Kotlin专属的特性:如扩展函数,声明点泛型变化以及本地函数。KSP API根据Kotlin的语法,把Kotlin程序拆解为各种静态的符号,能够处理如类,成员,函数,参数 以及注解等等。但它并不是运行时的(那是反射做的工作),因而像逻辑如循环和条件语句是没有办法进行处理,以及也无法得到表达式的成果。
虽然KSP是编译器插件,但它是运行在最终编译之前,也就是说在编译器编译全部代码之前,事先会运行KSP插件。所以KSP API最适合做的工作是:
- 读取代码和各种资源文件,并进行分析
- 生成代码
接下来看怎么详细运用KSP API。
装备KSP
KSP是由谷歌开发的一套东西,包含两部分一个是Kotlin plugin,另一个是依赖库。所以需求在项目的根build.gradle里面,先把plugin增加到项目里:
// The root build.gradle of your project
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.9.23' apply false
id 'com.google.devtools.ksp' version '1.9.23-1.0.20' apply false
}
当然,这一步其实并不是有必要的,也能够在每个模块中再装备plugin。
接下来,在运用KSP的模块里面增加plugin,增加依赖以及指明KSP processor,这是最为关键的装备:
// module build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm'
id 'com.google.devtools.ksp'
}
dependencies {
implementation project(':kspannotation')
ksp project(':kspprocessor')
}
如果项目顶层指定了plugin的版别,那么到了module这儿,就不用再指定版别了。别的就是要留意版别的匹配,ksp的版别前半段『1.9.23』指明 的是最低的Kotlin版别要求。最好是让ksp要求的版别与指定的Kotlin版别匹配或许距离较小,不然或许会有问题。dependencies中的ksp指定的是KSP processor,关于有些库或许注解和界说和KSP的processor或许会在同一个包里,那么写一句就够了,如Room的,就一句:ksp ‘androidx.room:room-compiler:2.6.1’。
如果是自界说的processor,需求为processor单独建一个library module,装备ksp库为依赖即可:
// KSP processor module build.gradle
plugins {
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation project(':kspannotation')
implementation 'com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.20'
implementation 'com.squareup:kotlinpoet-ksp:1.16.0'
}
典型的KSP procesor(包含网上大部分的比如)都是分了三个module,一个是界说注解的module,一个是完成processor的,一个是运用注解和processor的。但这并不是有必要的,为了便利,其实把注解的界说和processor放在一个module就能够了。只要把processor与运用它的module分隔来了,就能够。
留意:关于processor module来说它的类型要是library,而且要是Java library或许Kotlin library,由于这是Kotlin言语层面的东西。关于Android同学来说在新建module时一定要选择『Java or Kotlin Library』。
完成KSP Processor
装备好了模块后,剩余的就是要完成一个KSP processor了。
完成Processor
大部分工作plugin现已做好了,咱们需求做的就是完成一些接口。有两个需求完成,一个是SymbolProcessorProvider,另一个则是SymbolProcessor。
SymbolProcessorProvider适当于是processor的一个工厂办法,咱们完成它的create办法,回来一个SymbolProcessor实例,一个典型的完成:
class MyProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return MyProcessor(environment.codeGenerator)
}
}
它就适当于一个工厂办法,把上下文环境传给processor,SymbolProcessor是要点,咱们需求完成它的process办法,针对感兴趣的符号进行处理,比如用KotlinPoet生成代码,这儿是发挥创造力的地方:
class MyProcessor(private val generator: CodeGenerator) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val annotatedClasses = resolver
.getSymbolsWithAnnotation(MyAnnotation::class.java.name)
.filterIsInstance<KSClassDeclaration>()
for (aclass in annotatedClasses) {
val packageName = aclass.packageName.asString()
val className = aclass.simpleName.asString()
val methods = aclass.getDeclaredFunctions())
// ...
}
return emptyList()
}
}
注册Processor
完成了process后还需求把process注册一下,不然ksp plugin无法找到这个processor。在processor module与代码同级文件夹下新建文件『resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider』,然后把方才完成的provider的完整类名,写在文件里,如果是运用IDE一般都会有提示的。
// myprocessor/src/main/
// |-- kotlin/net/toughcoder/
// |-- MyProcessorProvider.kt
// |-- MyProcessor.kt
// |-- resources/META-INF/services/
// |-- com.google.devtools.ksp.processing.SymbolProcessorProvider
// file: resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
net.toughcoder.MyProcessorProvider
为啥要用KSP
现在来说KSP最主要应用仍然 是注解的处理,以及合作注解进行代码生成。通过前面一篇关于注解的文章中咱们知道,注解的处理现已有了一个专门的东西了叫做kapt,就现在来说KSP能做的工作kapt也都能做,它们都是用于编译时代码处理以及代码生成,都能处理注解。那么,在现已有了kapt的前提下,为啥还要搞KSP呢?
kapt虽然是Kotlin的注解处理器,但是它坚持Java的兼容性,它直接复用了Java的AbstractProcessor,要依赖于Java的annotation procssor以及javac,只适用于Kotlin/JVM,其他target用不起来,因而它并不能算是Kotlin原生的东西,对Kotlin的特性支撑不友爱。再有就是,为了坚持与javac的兼容性,它的处理速度很慢,有必要先把Kotlin代码转成javac能认识的规范Java代码,这必定会有不用要的性能开销。根据这些限制,kapt现已中止开发了,处于维护状况了,不会再增加新功能了。省流点来说,kapt是以Java视点来看待输入代码的(即也要处理的源码),而KSP是以Kotlin视点。
KSP则是Kotlin原生的,根据Kotlin开发的,且是为了Kotlin开发的,并不受限于javac,因而一切的Kotlin目标平台都能用。而且对Kotlin的特性支撑的很友爱。它的处理速度也较kapt有提高,由于不用要做编码转换了,省了一道工序。从官方给出的数据看至少能省25%的编译时刻。别的,KSP的API运用起来更加的Kotlin友爱一些SymbolProcessor传递过来的Resolver有很便利的接口能够取得被标的类,而且符号对象是KSClassDeclaration,它能够便利的取一个Kotlin类的相关的其他符号,如包名,类名,办法等。
总结
通过本文咱们了解了KSP的概念,并学会了怎么在项目中装备KSP, 以及怎么完成一个KSP processor。KSP视Kotlin代码为一系列的静态符号,对Kotlin言语特性支撑友爱,处于活泼的开发状况且被官方大力支撑,因而应该尽早转向KSP。而且相信KSP能做的工作会越来越多。
参考资料
- Kotlin Symbol Processing API
- Migrate from kapt to KSP
- Write a Symbol Processor with Kotlin Symbol Processing
- An Introduction to Kotlin Symbol Processing
- Kotlin Symbol Processing
欢迎搜索并关注 大众号「稀有猿诉」 获取更多的优质文章!
原创不易,「打赏」,「点赞」,「在看」,「保藏」,「分享」 总要有一个吧!