- Annotation 注解:注解是元数据,即描述数据的数据
- APT(Annotation Processing Tool)注解处理器
APT工作原理
Demo介绍
APT项目地址
使用APT maven仓库地址
(1)项目配置
- Gradle 8.2
- AGP 8.2.0
- Java jdk 17
- Kotlin 1.9.0
(2)Demo介绍
- 我们将自定义一个注解LeonDestination
MainActivity
TestActivity
- 我们要实现通过 url 进行 activity 的跳转,如下通过url实现从MainActivity跳转至TestActivity
1、实现注解模块
- 在根目录下,新建一个 leon-ann 文件夹
- 在leon-ann文件夹下新建一个build.gradle文件
build.gradle
//利用java插件、kotlin插件
apply plugin: 'java'
apply plugin: 'kotlin'//源码兼容性 java jdk 17
java {sourceCompatibility = JavaVersion.VERSION_17targetCompatibility = JavaVersion.VERSION_17
}
- 在根目录下的setting.gradle中加入leon-ann模块
setting.gradle
rootProject.name = "GradleApt"
include ':app'
include ':leon-ann'
- 在leon-ann文件夹下新建一个src/main/java/com.leon.ann目录
- 在com.leon.ann目录下新建一个Annotation类 LeonDestination
//限定作用于类
@Target(AnnotationTarget.CLASS)
//编译期可见
@Retention(AnnotationRetention.BINARY)/*** @param url 路由* @param description 描述*/
annotation class LeonDestination(val url: String,val description: String
)
2、实现注解处理器模块
- 在根目录下,新建一个 leon-processor文件夹
- 在leon-ann文件夹下新建一个build.gradle文件
build.gradle
//利用java插件、kotlin插件
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'//源码兼容性 java jdk 17
java {sourceCompatibility = JavaVersion.VERSION_17targetCompatibility = JavaVersion.VERSION_17
}dependencies{//依赖注解子工程implementation project(':leon-ann')//注册注解处理器implementation 'com.google.auto.service:auto-service:1.0-rc6'kapt 'com.google.auto.service:auto-service:1.0-rc6'//生成kotlin类implementation 'com.squareup:kotlinpoet:1.11.0'
}
- 在根目录下的setting.gradle中加入leon-processor模块
setting.gradle
rootProject.name = "GradleApt"
include ':app'
include ':leon-ann'
include ':leon-processor'
- 在leon-processor文件夹下新建一个src/main/java/com.leon.processor目录
- 在com.leon.processor目录下新建一个类 LeonProcessor
//源码兼容性 java jdk 17
@SupportedSourceVersion(SourceVersion.RELEASE_17)
//自动生成注解处理器
@AutoService(Processor::class)
class LeonProcessor : AbstractProcessor() {//TAGcompanion object {const val TAG = "LeonProcessor"}//告诉编译器,当前注解处理器支持的注解类型 - LeonDestinationoverride fun getSupportedAnnotationTypes(): MutableSet<String> {return Collections.singleton(LeonDestination::class.java.canonicalName)}/*** 编译器找到我们关心的注解后,会调用的方法** @param set 编译器搜集到的注解信息* @param roundEnvironment 编译环境*/override fun process(set: MutableSet<out TypeElement>?,roundEnvironment: RoundEnvironment?): Boolean {roundEnvironment?.let { env ->if (env.processingOver()) {//编译结束了,就直接返回return false}log("LeonProcessor 开始了")//拿到所有标记了 @LeonDestination 注解的 类的信息val allLeonDestinationElements =env.getElementsAnnotatedWith(LeonDestination::class.java)allLeonDestinationElements?.let { elements ->log("使用LeonDestination注解的类,有${elements.size}个")if (elements.size > 0) {//组装类信息val stringBuffer = StringBuffer()stringBuffer.append("val mapping = HashMap<String, String>()\n")elements.forEach { element ->val typeElement = element as TypeElement//在当前类上,获取 @LeonDestination 信息val leonDestination = typeElement.getAnnotation(LeonDestination::class.java)leonDestination?.let { destination ->//当前类的全名val realPath = typeElement.qualifiedName.toString()//当前类注解信息val url = destination.urlval description = destination.descriptionlog("realPath: $realPath")log("url: $url")log("description: $description")stringBuffer.append("mapping[\"$url\"] = \"$realPath\"\n")}}stringBuffer.append("return mapping\n")//生成类到本地文件中try {val packageName = "com.leon.apt"val className = "LeonDestinationMapping"val fileBuilder = FileSpec.builder(packageName, className)val classBuilder = TypeSpec.classBuilder(className).addType(TypeSpec.companionObjectBuilder().addFunction(FunSpec.builder("getMapping").returns(Map::class.parameterizedBy(String::class,String::class)).addStatement(stringBuffer.toString()).build()).build()).build()fileBuilder.addType(classBuilder)val kaptKotlinGeneratedDir = processingEnv.options["kapt.kotlin.generated"]fileBuilder.build().writeTo(File(kaptKotlinGeneratedDir))} catch (e: Exception) {log("生成类失败:${e.message}")}}}}log("LeonProcessor 结束了")return false}//输出日志信息private fun log(msg: String?) {msg?.let { m ->System.out.println("$TAG >>>>>> $m")}}
}
- 在app目录下的build.gradle加入leon-ann、leon-processor依赖
id "kotlin-kapt"
implementation project(':leon-ann')kapt project(':leon-processor')
- 在MainActivity上使用注解LeonDestination
- 在命令行中执行以下命令
注意,执行命令行时,先执行以下"gradle -v"、"java -version"命令,确保gradle是8.2,java jdk是17
//清理项目
./gradlew clean -q//构建项目
./gradlew :app:assembleDebug
执行效果
在app/build目录下生成了类LeonDestinationMapping
3、发布maven仓库
- 在根目录下的gradle.properties中,添加以下代码
LEON_MAVEN_PATH=../leon-maven
LEON_MAVEN_GROUP_ID=com.leon.apt
LEON_MAVEN_VERSION=1.0.0
- 在leon-ann目录下新建gradle.properties
LEON_MAVEN_ARTIFACT_ID=leon-apt-ann
- 在leon-processor目录下新建gradle.properties
LEON_MAVEN_ARTIFACT_ID=leon-apt-processor
- 在根目录下新建 leon-maven-publish.gradle
//引用 maven 插件,用于发布
apply plugin: 'maven-publish'//读取 rootProject 中的配置属性
Properties rootProjectProperties = new Properties()
rootProjectProperties.load(project.rootProject.file('gradle.properties').newDataInputStream())
def LEON_MAVEN_PATH = rootProjectProperties.getProperty("LEON_MAVEN_PATH")
def LEON_MAVEN_GROUP_ID = rootProjectProperties.getProperty("LEON_MAVEN_GROUP_ID")
def LEON_MAVEN_VERSION = rootProjectProperties.getProperty("LEON_MAVEN_VERSION")//读取子工程中的配置属性
Properties projectProperties = new Properties()
projectProperties.load(project.file("gradle.properties").newDataInputStream())
def LEON_MAVEN_ARTIFACT_ID = projectProperties.getProperty("LEON_MAVEN_ARTIFACT_ID")//输出日志
log("LEON_MAVEN_PATH: $LEON_MAVEN_PATH")
log("LEON_MAVEN_GROUP_ID: $LEON_MAVEN_GROUP_ID")
log("LEON_MAVEN_VERSION: $LEON_MAVEN_VERSION")
log("LEON_MAVEN_ARTIFACT_ID: $LEON_MAVEN_ARTIFACT_ID")//发布信息填写
publishing {publications {mavenJava(MavenPublication) {//设置groupId,通常为当前插件的包名groupId = LEON_MAVEN_GROUP_ID//设置artifactId,作为当前插件名称artifactId = LEON_MAVEN_ARTIFACT_ID//设置插件版本号version = LEON_MAVEN_VERSIONlog("pom信息groupId: $groupId")log("pom信息artifactId: $artifactId")log("pom信息version: $version")// 指定要发布的组件,例如Java库或插件等from components.java}}repositories {maven {//设置发布路径为 工程根目录下的 leon-publish 文件夹url = uri(LEON_MAVEN_PATH)}}
}//日志输出
def log(String msg) {println("maven发布 >>>>>> $msg")
}
- 在leon-ann目录下的build.gradle引用 leon-maven-publish.gradle
apply from: rootProject.file("leon-maven-publish.gradle")
- 执行命令
./gradlew clean -q
./gradlew :leon-ann:publish
执行结果
根目录下生成maven库
- 在leon-processor目录下的build.gradle引用 leon-maven-publish.gradle
apply from: rootProject.file("leon-maven-publish.gradle")
- 执行命令
./gradlew clean -q
./gradlew :leon-ann:publish
执行结果
根目录下生成maven库
4、在其他项目中引用maven仓库
- 新建一个新的项目Application1
- 在Application1的根目录下setting.gradle中引入leon-maven仓库
//引入leon-maven
maven {url uri("/Users/leon/AndroidStudioProjects/GradleLearn/apt/leon-maven")
}
- 在app目录下build.gradle中引入leon-ann和leon-processor
//groupId : artifactId : versionimplementation 'com.leon.apt:leon-apt-ann:1.0.0'kapt 'com.leon.apt:leon-apt-processor:1.0.0'
- 在MainActivity和TestActivity中使用LeonDestination注解
- 执行命令行
./gradlew clean -q
./gradlew :app:assembleDebug -q
执行效果
app/build目录下生成了类LeonDestinationMapping
- 在MainActivity中实现跳转代码
import com.leon.ann.LeonDestination
import com.leon.apt.LeonDestinationMapping@LeonDestination(url = "leon://page-main",description = "这是主页"
)
class MainActivity : AppCompatActivity() {private var mBtn: TextView? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)mBtn = findViewById<TextView?>(R.id.id_btn_skip).apply {setOnClickListener {//点击事件LeonDestinationMapping.getMapping()["leon://page-test"]?.let { des ->//跳转startActivity(Intent().apply {setClassName(packageName,des)})}}}}
}