• Gradle学习-6 APT 实现一个路由跳转框架(APT、发布maven仓库)


    • 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()\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
                            )
                        })
                    }
                }
            }
        }
    }
    
  • 相关阅读:
    如何打印 springboot 框架中 接收请求的日志
    MySQL 练习<1>
    二叉树和堆
    ABB机器人:工件坐标系介绍以及标定操作与使用方法
    【iMessage苹果推日历推位置推送】软件安装 UIApplication 的 registerForRemoteNotifications
    模拟 Junit 框架
    使用pywin32读取doc文档的方法及run输出乱码 \r\x07
    提升用户体验的利器:揭秘Spring框架中国际化的奇妙魔力
    [Codeforces] combinatorics (R1200) Part.1
    Day 47 MySQL Navcat、PyMYsql
  • 原文地址:https://blog.csdn.net/weixin_41733225/article/details/140276918