开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 22 天,点击查看活动详情
通过与与KAPT比较, 简要介绍KSP(Kotlin符号处理). KSP准备好大规模应用了吗?
要使你的代码更容易并跳过大量的模板代码, 代码生成是最有用的功能之一. 花在编译上的时间增加了, 但你的代码更紧凑, 更容易支持. Kotlin源文件不能直接用于注释处理, 所以在KAPT中实现了某种变通方法来跳过这个限制. 这种变通方法也会增加编译时间, KSP(Kotlin符号处理)是由谷歌开发的, 用于解决这些问题. 它很厉害, 但还没有准备好让开发者大规模采用, 我在最后解释了原因.
代码生成的含义
主要目标是一个或多个文件被转换为代码.
R.class就是一个使用这种机制的好例子. Android Gradle插件解析XML文件(strings.xml, values.xml等), 以便得到包含XML文件中所有键的R.class. 该插件对如何组合这些文件有一定的规则, 例如, 来自strings.xml的键转换为R.strings.{key}.
在3.6版本之前, 该插件已经生成了
R.java源代码. 从3.6开始, 该插件生成了一个字节码(加快了编译时间).
对于源文件, 同样的技巧能够做到. 它被称为注解处理器, 注解被用作标记, 在处理过程中可以使用.
首先, 注解是什么意思?
在注释处理API(JSR-175, 2006)之前, 注释已经被引入Java(JSR-175, 2004). 它们能够将Java类型的元数据保留到Java源文件中.
有三个目标:
- SOURCE 只在编译阶段可用, Java编译器将它们从编译的JVM(Java虚拟机)字节码中删除.
- CLASS 被存储到JVM字节码中, 以便在将字节加载到VM(虚拟机)之前对字节码进行操作, 但它在Runtime中不可用.
- RUNTIME 通过反射在Runtime中可用, 它能够在不重新编译的情况下扩展功能, 并在飞行中完成.
每个Kotlin类都有
kotlin.Metadata注解. 该注解有RUNTIME目标. Kotlin编译器在编译时使用它们, 并在Runtime中使它们可用.
再来看看注解处理器. 注释处理器是可插拔的注释处理API(JSR-269)的实现. 其主要思想与XML文件相同, 其中注释是用来代替键的. 向你展示空的注释处理器代码.
class TestProcessor : AbstractProcessor() {
// Denotes maximum supported Java version
override fun getSupportedSourceVersion(): SourceVersion {
return SourceVersion.latestSupported()
}
// Denotes annotations that will be processed
override fun getSupportedAnnotationTypes(): MutableSet<String> {
return mutableSetOf(Builder::class.java.name)
}
// Called by Java compiler to process current round
override fun process(
// Contains found annotation in current round
annotations: MutableSet<out TypeElement>?,
// Gives access to Java compiler details
// and contains a result from previous round
roundEnv: RoundEnvironment?
): Boolean { /* Perform annotation processing here */ }
}
首先, Java编译器分析所有的源文件和所有的依赖关系并建立AST(抽象语法树). 试着想象一下, 你写的所有代码看起来就像一棵树, 对编译器来说有很多分支. 树上的每个元素都被称为TypeElement.
注释处理器是编译器的一个插件, 所以当编译器调用处理器时, 会使用相同的AST.
一个注释处理器有以下方法:
getSupportedSourceVersion表示最大支持哪种Java语言版本.getSupportedAnnotationTypes返回要处理的注解.process返回一个圆形的处理结果.
另外, process有2个参数:
annotations- 此轮的注释被发现.roundEnv- 这是支持的实体, 可以访问编译器数据并获得前一轮的结果.
一轮的含义与一个周期中的迭代相同, 在这个周期中, 编译器会多次迭代代码以解决所有的依赖关系.
为了处理注解, 处理器会执行几轮处理, 为什么?
- 一个处理器可以生成新的类型, 里面有注解, 它们也会被处理.
- 类型元素可以是无效的, 例如依赖新生成的类型, 在无效的时候跳过几轮.
与此同时, Kotlin开始在Android开发中越来越受欢迎, Google正式将Kotlin作为Android的支持语言. Kotlin有兼容的JVM字节码. 这意味着Java和Kotlin的编译器将源文件编译成单一的文件格式, 对虚拟机来说是可以理解的.
没有注释处理器, Kotlin就不能被大规模采用. 而且我们不能使用相同的注释处理器, Kotlin和Java有不同的语法, 但处理器是用Java AST工作的, 不理解Kotlin AST. JetBrains发布的KAPT已经解决了这个限制.
KAPT使用了某种变通方法, 它允许对Kotlin源文件重新使用已经写好的注释处理器.
敬请期待KAPT和KSP的内容...
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 22 天,点击查看活动详情