前言
了解过Compose
的同学都知道,只需要添加一个@Compose
注解就可以将函数转化成Compose
函数,同时Compose
函数也只能在Compose
函数中运行。这看起来似乎跟协程比较像,@Compose
是不是也像协程一样,往函数中添加了一些参数呢?
我们就一起来看下,@Compose
到底做了什么,又是怎么做到的。
前置知识
一看到@Compose
注解,我们很容易就想到注解处理器,但是@Compose
的解析并不是通过注解处理器来实现的,因为注解处理器只能生成代码,不能修改代码
而KCP
(Kotlin Compiler Plugin
):即kotlin
编译插件,支持跨平台,android
开发可以将它类比为kapt
+transform
机制,既可以生成代码,也可以修改代码
@Compose
注解的解析就是通过KCP
来实现的
什么是KCP
Kotlin
编译过程简单来说,就是将Kotlin
源码编译成字节码的过程,具体步骤如下所示:
而Kotlin
编译期插件则在编译过程中提供Hook
时机,让我们可以解析符号,修改字节码生成结果等。
Kotlin
库中的不少语法糖都用到了KCP
,比如Kotlin-android-extension
,@Parcelize
等,@Compose
注解同样也是通过KCP
解析的
相比KAPT
,KCP
主要有以下优点:
KAPT
是基于注解处理器的,它需要将Kotlin
代码转化成Stub
再解析注解生成代码,常常转化成Stub
的时间比生成代码还要长,而KCP
则是直接解析Kotlin
的符号,因此在编译速度上KCP
比KAPT
要强的多KAPT
只能生成代码,不能修改代码,而KCP
不仅可以生成代码,也可以修改代码,可以看作是kapt
+transorm
机制
而KCP
的缺点则在于,KCP
的开发成本太高,涉及 Gradle Plugin
、Kotlin Plugin
等的使用,API
涉及一些编译器知识的了解,一般开发者很难掌握。
因此如果只是需要处理注解生成代码,不需要修改代码,通常使用KSP
就足够了,KSP
是对KCP
的一个封装,如果对KSP
的使用详情感兴趣可参见:告别KAPT!使用 KSP 为 Kotlin 编译提速
KCP
的基本概念
上面也说到了,KCP
的开发成本较高,主要包括以下内容:
Plugin
:Gradle
插件用来读取Gradle
配置传递给KCP
(Kotlin Plugin
)Subplugin
:为KCP
提供自定义KP
的maven
库地址等配置信息CommandLineProcessor
:负责将Plugin
传过来的参数转换并校验ComponentRegistrar
:负责将用户自定义的各种Extension
注册到KP
中,并在合适时机调用
ComponentRegistrar
是核心入口,所有的KCP
自定义功能都需要通过这个类注册一些Extension
接口来实现。
下面列举一些常用的Extension
接口,大家可以根据需求选用:
IrGenerationExtension
,用于增/删/改/查代码DiagnosticSuppressor
,用于抑制语法错误,Jetpack Compose
有使用StorageComponentContainerContributor
,用于实现IOC
@Compose
注解的作用
上面介绍了KCP
的基本概念,下面看下在Jetpack Compose
中@Compose
注解到底是怎么解析的,又起了什么作用
注册IrGenerationExtension
上面我们介绍了ComponentRegistrar
是核心入口,负责将用户自定义的各种Extension
注册到KP
中,并在合适时机调用,而IrGenerationExtension
可以用于修改代码
Compose
插件的入口为