android2024 gradle8 Processor和ksp两种编译时注解实现

android的编译时注解,老生常谈,外面的例子都是bindView,脑壳看疼了,自己学习和编写下。
而且现在已经进化到kotlin2.0了,google也逐渐放弃kapt,进入维护状态。所以要好好看看本贴。
参考我的工程:
https://github.com/jzlhll/AndroidComponts
ClassNameAnnotations
ClassNameAnnotations-compiler
ClassNameAnnotations-ksp
app
四个模块参考。

一、kotlin项目+kapt

1. 新建注解的单独模块,注意是java/kotlin library:

请添加图片描述
配置gradle为:

plugins {
    id 'java-library'
    id 'kotlin'
}

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

添加自定义注解类:

@Retention(RetentionPolicy.CLASS)
@Target(value = ElementType.TYPE)
public @interface EntroFrgName {
}

这是我的需求,目的就是标记一个类,用来收集所有标注了注解的类,把他们收集成一个List。

2.再创建一个compiler模块,也是java/kotlin library:

请添加图片描述
得到2个模块了。

2.1 gradle:
plugins {
    id 'java-library'
    id 'kotlin'
}

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

dependencies {
    implementation project(':ClassNameAnnotations')
}
2.2 配置辅助文件:

这一步可以通过autoservice来省略。查看注意事项。
再main下面reosurces/META-INF/services/目录下,创建文件javax.annotation.processing.Processor
里面写上com.au.learning.classnamecompiler.MyProcessor ,
就是下面代码MyProcessor 的类路径。

2.3 编写注解解析代码:

class MyProcessor : AbstractProcessor() {
    private var processingEnv:ProcessingEnvironment? = null

    override fun init(processingEnv: ProcessingEnvironment?) {
        super.init(processingEnv)
        this.processingEnv = processingEnv
        processingEnv?.messager?.printMessage(Diagnostic.Kind.WARNING, "init...!")
    }

    /**
     * 所支持的注解合集
     */
    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return mutableSetOf(EntroFrgName::class.java.canonicalName)
    }

    private fun isElementInAnnotations(target:Element, annotations: Set<TypeElement>) : Boolean {
        for (annotation in annotations) {
            //匹配注释
            if (target == annotation) {
                return true
            }
        }
        return false
    }

    //Element代表程序中的包名、类、方法。即注解所支持的作用类型。
    fun getMyElements(annotations: Set<TypeElement>, elements: Set<Element?>): Set<TypeElement> {
        val result: MutableSet<TypeElement> = HashSet()
        //遍历包含的 package class method
        for (element in elements) {
            //匹配 class or interface
            if (element is TypeElement) {
                for (annotationMirror in element.annotationMirrors) {
                    val found = isElementInAnnotations(annotationMirror.annotationType.asElement(), annotations)
                    if (found) {
                        result.add(element)
                        break
                    }
                }
            }
        }
        return result
    }

    /**
     * @param annotations 需要处理的注解 即getSupportedAnnotationTypes被系统解析得到的注解
     * @param roundEnv 注解处理器所需的环境,帮助进行解析注解。
     */
    override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment?): Boolean {
        val elements = roundEnv?.rootElements?.let {
            if (annotations != null) {
                getMyElements(annotations, it)
            } else {
                null
            }
        }

        val names = AllEntroFragmentNamesTemplate()
        if (!elements.isNullOrEmpty()) {
            for (e in elements) {
                names.insert(e.qualifiedName.toString())
            }

            val code = names.end()
            processingEnv.filer?.let {
                try {
                    // 创建一个JavaFileObject来表示要生成的文件
                    val sourceFile: JavaFileObject = it.createSourceFile("com.allan.androidlearning.EntroList", null)
                    sourceFile.openWriter().use { writer ->
                        // 写入Java(或Kotlin)代码
                        writer.write(code)
                        writer.flush()
                    }
                } catch (e: IOException) {
                    processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate file: " + e.message)
                }
            }
        }

        return true
    }

    //一定要修改这里,避免无法生效
    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.latestSupported()
    }
}


class AllEntroFragmentNamesTemplate : AbsCodeTemplate() {
    private val insertCode = StringBuilder()

    /**
     * com.allan.androidlearning.activities.LiveDataFragment.class
     */
    fun insert(javaClass:String) {
        insertCode.append("list.add(").append(javaClass).append(".class);").appendLine()
    }

    fun end() : String {
        return codeTemplate.replace("//insert001", insertCode.toString())
    }

    override val codeTemplate = """
package com.allan.androidlearning;

import androidx.fragment.app.Fragment;

import java.util.ArrayList;
import java.util.List;

public class EntroList {
    public List<Class<? extends Fragment>> getEntroList() {
        List<Class<? extends Fragment>> list = new ArrayList<>();
        //insert001
        return list;
    }
}
    """.trimIndent()
}

这里有2个可以进一步学习的东西,一个是auto库来帮你生成META-INF文件;而是通过javapoet来生成文件。详细在注意事项里面。
我这里就为了方便,也减少学习成本,自行整一个模版代码(这个模版代码可以自己写好一个类,拷贝到string codeTemplate),把生成部分通过string.replace进行不就好了?
然后简单的通过processingEnv.filer.createSourceFile,write了就好了。我自认为是一个不错的办法。

3. 主工程

剩下就简单了,app/build.gradle修改:

	plugins {
	    id 'com.android.application'
	    id 'org.jetbrains.kotlin.android'
	    //id 'com.google.devtools.ksp'
	    id 'kotlin-kapt' //添加
	}
	...
    //my apt
    implementation project(':ClassNameAnnotations')
    //kotlin
    kapt project(':ClassNameAnnotations-compiler')
    //java工程换成annotationProcessor 
    //annotationProcessor project(':ClassNameAnnotations-compiler')

给代码添加自己的注解了:

@EntroFrgName
class CanvasFragment : ViewFragment() {

@EntroFrgName
class DialogsFragment : ViewFragment() {

编译:
调试过程,可以选择gradle->Tasks->other->kaptDebugKotlin来编译。比直接编译更快,更单一。
最后编译结果在:
请添加图片描述
再最后,你可以把这个类,拿去类似BuildConfig一样去引用了。

二、java工程

自然是用不了ksp的。
唯一的注意就是app/build.gradle:

    //java工程换成annotationProcessor 
    annotationProcessor project(':ClassNameAnnotations-compiler')

然后各个gradle中,删除kotlin相关的痕迹。略。

三、KSP

终于谈到ksp了。
跟上面kapt一样,创建2个java/kotlin的模块。一个注解模块,一个处理模块,(那个回调的compiler代表着settings.gradle已经不加载,不使用,不管它)。
请添加图片描述
注解模块的注解可以使用kotlin的注解类,也可以继续使用java的注解类。
区别只是在provider的解析代码上有一点点区别:

//EntroFrgName是java的注解类
resolver.getSymbolsWithAnnotation(EntroFrgName::class.java.canonicalName)
//EntroFrgName是kotlin的注解类
resolver.getSymbolsWithAnnotation(EntroFrgName::class.qualifiedName!!)
gradle:

根目录的build.gradle添加:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '8.4.2' apply false
    id 'com.android.library' version '8.4.2' apply false
    id 'org.jetbrains.kotlin.android' version "1.9.24" apply false
    id 'com.google.devtools.ksp' version '1.9.24-1.0.20' apply false
}

ksp这个模块的build.gradle为:

plugins {
    id 'java-library'
    id 'kotlin'
}

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

dependencies {
    implementation project(':ClassNameAnnotations')
    implementation('com.google.devtools.ksp:symbol-processing-api:1.9.24-1.0.20')
}

多引入了symbol-processing-api,注意kotlin.android, devtools.ksp与之相同对应,查看https://github.com/google/ksp/releases。

provider解析代码
class AllEntroFrgNamesProvider : SymbolProcessorProvider{
    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
        return TestKspSymbolProcessor(environment)
    }
}

/**
 * creator: lt  2022/10/20  lt.dygzs@qq.com
 * effect : ksp处理程序
 * warning:
 */
class TestKspSymbolProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor {
    // 使用一个集合来跟踪已经处理过的符号
    private val processedSymbols = mutableSetOf<KSDeclaration>()

    override fun process(resolver: Resolver): List<KSAnnotated> {
        environment.logger.warn("process start....")

        val symbols = resolver.getSymbolsWithAnnotation(EntroFrgName::class.java.canonicalName)
        val ret = mutableListOf<KSAnnotated>()

        val allEntroFragmentNamesTemplate = AllEntroFragmentNamesTemplate()
        var hasMy = false

        symbols.toList().forEach { symbol->
            if (!symbol.validate())
                ret.add(symbol)
            else {
                if (symbol is KSClassDeclaration && symbol.classKind == ClassKind.CLASS) {
                    val qualifiedClassName = symbol.qualifiedName?.asString()
                    allEntroFragmentNamesTemplate.insert(qualifiedClassName!!)
                    hasMy = true
//                    symbol.accept(TestKspVisitor(environment), Unit)//处理符号
                } else {
                    ret.add(symbol)
                }
            }
        }

        if (hasMy) {
            val code = allEntroFragmentNamesTemplate.end()

            // 生成文件
            val file = environment.codeGenerator.createNewFile(
                dependencies = Dependencies(false),
                packageName = "com.allan.androidlearning",
                fileName = "EntroList"
            )

            // 写入文件内容
            OutputStreamWriter(file).use { writer ->
                writer.write(code)
            }
        }

        //返回无法处理的符号
        return ret
    }
}
主工程app引入

类似前面kapt的,主工程app/build.gradle

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.google.devtools.ksp'
}


   //my apt
   implementation project(':ClassNameAnnotations')
   ksp project(':ClassNameAnnotations-ksp')

申明ksp和dependencies部分的引入即可,使用ksp的用法了。
最后添加注解最终生成的代码在:
请添加图片描述

注意事项

1. 注意点

1.1 最好打印日志warn。否则一般的android studio编译是默认不打印别的级别的。

//Processor
processingEnv?.messager?.printMessage(Diagnostic.Kind.WARNING, "init...!")
//ksp
environment.logger.warn("process start....")

1.2 kapt已经逐渐放弃,kt2已经开始不再维护kapt了。

1.3 很多人把kapt,annotationProcessor,ksp搞混。
我们可以看到,glide库:
请添加图片描述
它也是有2个process的模块的,一个是给老的kapt或者java(annotationProcessor)处理。一个是给ksp实现的。我们如出一辙。

2. 进一步学习

第一个,
是使用autoservice来自动注解MyProcessor ,让他帮我们生成这个META-INF里面的文件。这个autoservice就干这么点事情,不要过于神秘。compiler这个模块添加gradle(自己在这里看最新版本,https://github.com/google/auto):

 annotationProcessor 'com.google.auto.service:auto-service:1.11.0'
 implementation 'com.google.auto.service:auto-service-annotations:1.11.0'

然后给我们的Processor类添加上注解:

@AutoService(value = {Processor.class})

这纯属于是,我还没有编写完自己的注解, 就用上别的注解来给我的注解模块生成文件了。[手动狗头]。

第二个,使用javapoet来实现生成代码。需要自行了解他的api和class,函数的结构。

3. 坑了一天

出现一个问题,始终找不到原因。原来是
请添加图片描述
META-INF下面是目录services,再放一个文件。
而studio中显示的是跟包名一样。导致ksp的时候,搞了好久一直编译不过,提示[ksp] No providers found in processor classpath。好在有这句话,终于在ksp下解决了。之前搞kapt,怎么都搞不好。也没有提示。最后基于这个目录问题,总算2个都编译通过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/774774.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

djangoGD高校信管专业就业信息管理系统-计算机毕业设计源码59343

djangoGD高校信管专业就业信息管理系统 摘 要 随着高校信管专业的快速发展&#xff0c;学生就业问题日益受到广泛关注。为了更好地服务学生&#xff0c;提高就业率&#xff0c;许多高校开始引入信息化手段来管理学生就业信息。然而&#xff0c;传统的就业信息管理方式存在很多问…

Linux关于文件的高级命令

tree命令 tree命令用于以树状图的形式显示目录结构。它可以帮助用户快速了解目录和文件的层次关系&#xff0c;非常适合用于浏览和理解大型文件系统的结构。 基础用法 显示当前目录的树状结构&#xff1a;tree 显示指定目录的树状结构&#xff1a;tree 指定目录路径 tree命…

【C++】 解决 C++ 语言报错:Segmentation Fault

文章目录 引言 段错误&#xff08;Segmentation Fault&#xff09;是 C 编程中常见且令人头疼的错误之一。段错误通常发生在程序试图访问未被允许的内存区域时&#xff0c;导致程序崩溃。本文将深入探讨段错误的产生原因、检测方法及其预防和解决方案&#xff0c;帮助开发者在…

昇思25天学习打卡营第5天 | 神经网络构建

1. 神经网络构建 神经网络模型是由神经网络层和Tensor操作构成的&#xff0c;mindspore.nn提供了常见神经网络层的实现&#xff0c;在MindSpore中&#xff0c;Cell类是构建所有网络的基类&#xff0c;也是网络的基本单元。一个神经网络模型表示为一个Cell&#xff0c;它由不同…

如何摆脱反爬虫机制?

在网站设计时&#xff0c;为了保证服务器的稳定运行&#xff0c;防止非法数据访问&#xff0c;通常会引入反爬虫机制。一般来说&#xff0c;网站的反爬虫机制包括以下几种&#xff1a; 1. CAPTCHA&#xff1a;网站可能会向用户显示CAPTCHA&#xff0c;要求他们在访问网站或执行…

Netty学习(Netty入门)

概述 Netty是什么 Netty的地位 Netty的优势 HelloWorld public class HelloClient {public static void main(String[] args) throws InterruptedException {// 1. 启动类new Bootstrap()// 2. 添加 EventLoop.group(new NioEventLoopGroup())// 3. 选择客户端 channel 实现.…

python绘制领域矩形

问题描述&#xff1a; 使用python书写代码实现以下功能&#xff1a;给定四个点的坐标&#xff0c;调用一个函数&#xff0c;可以使原来的四个点分别向四周上下左右移动15距离&#xff0c;分别记录下移动后的坐标&#xff0c;然后画出内侧矩形和外侧矩形 代码&#xff1a; im…

配置并调试后端程序(sql)

1.环境准备 安装VS Code和Node.js插件&#xff1a;确保你已经安装了VS Code和Node.js插件。创建launch.json文件&#xff1a;在你的项目中创建一个.vscode文件夹&#xff0c;并在其中创建launch.json文件。添加以下内容&#xff1a; {"version": "0.2.0"…

【C语言】五子棋(c语言实现)

这里写目录标题 最终效果菜单打印函数棋盘的初始化和打印人人对战落子判空函数悔棋函数判胜负函数人人对战 人机对战一是将直接调用rand生成随机值&#xff0c;这就不可控二是根据棋子赢面来判断哪里落子最好 如果选择退出程序直接exit就行主函数调用逻辑源代码 最终效果 五子棋…

The Sandbox 人物化身每月奖励: 七月版来了!

人物化身的持有者可以从 The Sandbox 领取自己的队服&#xff01; 视频&#xff1a;https://youtu.be/tSo5FPL7DhE 我们又推出了人物化身所有者月度奖励&#xff01;在七月&#xff0c;我们将通过 The Sandbox 队服来弘扬体育竞技精神。穿上这些时尚的元宇宙队服&#xff0c;代…

深度报告 | 百度安全携手极越安全发布《整车安全渗透测试白皮书》

注重点&#xff0c;如何确保车辆全生命周期的安全已成为整个行业亟待解决的问题。对于车企而言&#xff0c;通过渗透测试尽量多地发现安全威胁&#xff0c;是确保车辆信息系统的稳定运行、保障用户安全驾驶至关重要的措施。然而&#xff0c;传统的渗透测试方法已无法满足智能网…

Linux miniconda 安装tensorflow-gpu遇到找不到GPU问题

背景&#xff1a; Linux Miniconda python3.9 安装步骤 1、 pip install tensorflow-gpu2.8.0 -i https://pypi.tuna.tsinghua.edu.cn/simple 2、报错如下&#xff1a; 更换镜像源&#xff0c;单独安装 pip install tf-estimator-nightly2.8.0.dev2021122109 -i https:/…

使用 docker buildx 构建跨平台镜像

buildx是Docker官方提供的一个构建工具&#xff0c;它可以帮助用户快速、高效地构建Docker镜像&#xff0c;并支持多种平台的构建。使用buildx&#xff0c;用户可以在单个命令中构建多种架构的镜像&#xff0c;例如x86和arm架构&#xff0c;而无需手工操作多个构建命令。此外bu…

【docker】容器内配置环境变量

背景&#xff1a; 我要把下面的环境变量写到bash脚本里&#xff0c;起名叫environment_start.sh。 目的&#xff1a; 用于每次进入容器dev_into.sh的时候&#xff0c;让系统获取到环境变量。 操作步骤&#xff1a; 先在容器外找个合适的位置写环境变量bash脚本&#xff0c…

bmob Harmony鸿蒙快速开发搜索功能

搜索功能是很多应用都需要的功能。在很多平台上&#xff0c;要开发一个兼容性较好的搜索功能都还是需要添加比较多的视图代码的。 为了解决这个问题&#xff0c;鸿蒙ArkUI提供了一个快速添加搜索功能的视图组件给我们&#xff0c;结合Bmob Harmony鸿蒙SDK的搜索能力&#xff0…

四款主流电脑监控软件(电脑监控软件主要优势)

在现代企业环境中&#xff0c;确保员工的工作效率和企业信息的安全成为了管理者的重要任务。电脑监控软件作为一种有效的管理工具&#xff0c;能够帮助企业实现这些目标。固信电脑监控软件在这方面表现尤为出色&#xff0c;本文将详细介绍固信电脑监控软件的优势及其主要功能&a…

【C++】 解决 C++ 语言报错:Use of Uninitialized Variable

文章目录 引言 使用未初始化的变量&#xff08;Use of Uninitialized Variable&#xff09;是 C 编程中常见且危险的错误之一。它通常在程序试图使用尚未赋值的变量时发生&#xff0c;导致程序行为不可预测&#xff0c;可能引发运行时错误、数据损坏&#xff0c;甚至安全漏洞。…

基于芯片CSU8RP1382开发的咖啡秤方案

咖啡电子秤芯片方案精确值可做到分度值0.1g的精准称重,并带有过载提示、自动归零、去皮称重、压低报警等功能&#xff0c;工作电压在2.4V~3.6V之间&#xff0c;满足于咖啡电子秤的电压使用。同时咖啡电子秤PCBA设计可支持四个单位显示&#xff0c;分别为&#xff1a;g、lb、oz、…

云仓酒庄天津分公司:深化业务常态化运营

标题&#xff1a;云仓酒庄天津分公司&#xff1a;深化业务常态化运营&#xff0c;以沙龙为纽带&#xff0c;构建价值叠加的酒业新生态 在当今复杂多变的经济环境中&#xff0c;传统酒业面临着前所未有的挑战与机遇。随着数字化转型的加速和消费者偏好的日益多元化&#xff0c;…

JAVA--JSON转换工具类

JSON转换工具类 import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackso…