- 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_17
targetCompatibility = 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_17
targetCompatibility = 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() {
//TAG
companion object {
const val TAG = "LeonProcessor"
}
//告诉编译器,当前注解处理器支持的注解类型 - LeonDestination
override 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.url
val description = destination.description
log("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_VERSION
log("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 : version
implementation '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? = null
override 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
)
})
}
}
}
}
}