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

配置build.gradle:
plugins {id 'java-library'id 'kotlin'
}java {sourceCompatibility = JavaVersion.VERSION_17targetCompatibility = JavaVersion.VERSION_17
}
添加自定义注解的java类:
@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_17targetCompatibility = 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? = nulloverride fun init(processingEnv: ProcessingEnvironment?) {super.init(processingEnv)this.processingEnv = processingEnvprocessingEnv?.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 methodfor (element in elements) {//匹配 class or interfaceif (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<>();//insert001return list;}
}""".trimIndent()
}
这里有2个可以进一步学习的东西,一是auto库帮你生成META-INF文件。
二是通过javapoet来生成文件。详细在文章末尾注意事项。
本质上APT的目的就是将未知的代码,写成一个具体的类,被现有代码去调用,我自然可以直接写出这个类。所以,我为了方便和减少学习成本,自行整了一个模版代码(这个模版代码可以自己写好一个类,拷贝到string codeTemplate),把生成部分通过string.replace处理即可。然后简单地通过processingEnv.filer.createSourceFile,write就可以完成,自认为是一个不错的办法。
3. 主工程
剩下就简单了,app/build.gradle修改:
plugins {id 'com.android.application'id 'org.jetbrains.kotlin.android'id 'kotlin-kapt' //添加}...//注解引如implementation project(':ClassNameAnnotations')//kotlinkapt project(':ClassNameAnnotations-compiler')//java工程换成annotationProcessor //annotationProcessor project(':ClassNameAnnotations-compiler')
给代码添加自己的注解了:
@EntroFrgName
class CanvasFragment : ViewFragment() {@EntroFrgName
class DialogsFragment : ViewFragment() {
编译:
调试过程,可以选择gradle->Tasks->other->kaptDebugKotlin来编译。比直接编译更快,更单一。
编译结果在:

再最后,把这个类,拿去类似BuildConfig一样去调用了。至此已经完成。
二、app模块是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!!)
1. gradle:
根目录的build.gradle添加:
plugins {id 'com.android.application' version '8.4.2' apply falseid 'com.android.library' version '8.4.2' apply falseid 'org.jetbrains.kotlin.android' version "1.9.24" apply falseid '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_17targetCompatibility = JavaVersion.VERSION_17
}dependencies {implementation project(':ClassNameAnnotations')implementation('com.google.devtools.ksp:symbol-processing-api:1.9.24-1.0.20')
}
注意kotlin.android, devtools.ksp与symbol-processing-api三者的版本对应,查看https://github.com/google/ksp/releases。
2. 配置解析器辅助文件:
src/main/resources/META-INF/services/目录下:
com.google.devtools.ksp.processing.SymbolProcessorProvider 文件。写下如下的名字。
com.au.learning.classnamecompiler.AllEntroFrgNamesProvider。就是下面的类名。
3. 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 = falsesymbols.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}
}
4. 主工程app引入
类似前面kapt的,主工程app/build.gradle
plugins {id 'com.android.application'id 'org.jetbrains.kotlin.android'id 'com.google.devtools.ksp'
}implementation project(':ClassNameAnnotations')ksp project(':ClassNameAnnotations-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.0开始不再努力维护kapt。尽量迁移ksp。更快更有支持。
1.3 很多人使用glide,经常把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,再放一个文件。之前搞成了META-INF.services这个错误的目录!
而studio中显示的却跟包名一样。导致ksp的时候,搞了好久一直编译不过,提示[ksp] No providers found in processor classpath。好在有这句话,终于在ksp下解决了,之后反推到kapt也解决了。之前搞kapt,怎么都搞不好,也没有提示。
相关文章:
android2024 gradle8 Processor和ksp两种编译时注解实现
android编译时注解,老生常谈,外面的例子都是bindView,脑壳看疼了,自己学习和编写下。 而且现在已经进化到kotlin2.0,google也逐渐放弃kapt,进入维护状态。所以要好好看看本贴。 参考我的工程: h…...
elementui的table的@selection-change阻止事件改变
说明: 最近有个不想说的(xxx)业务,在表格勾选每一行的时候要触发一系列查询功能,查询失败还要把那个勾勾回退。真实蛋疼!表格勾选的默认selection-change是change事件,一般change事件是在完成之…...
空间数据采集与管理:为什么选择ArcGISPro和Python?
你还在为找不到合适的数据而苦恼吗?你还在面对大量数据束手无策,不知如何处理吗?对于从事生产和科研的人员来说,空间数据的采集与管理是地理信息系统(GIS)和空间分析领域的关键环节。通过准确高效地采集和管…...
案例精选 | 聚铭综合日志分析系统为江苏省电子口岸构建高效安全的贸易生态
江苏省电子口岸有限公司,成立于2009年,由江苏省贸促会携手南京海关、江苏检验检疫局及江苏海事局等部门共同出资组建。公司承载着推动江苏乃至长三角地区国际贸易便利化的重大使命,致力于打造一个集先进性、创新性、高效性于一体的电子口岸综…...
TCP粘包
目录 TCP粘包产生的原因 TCP粘包的现象 TCP粘包的解决方案 TCP粘包是指在TCP通信中,发送方发送的多个数据包在接收方被错误地合并成一个数据包的现象。tcp粘包在发送端和接收端都有可能发生。发送端粘包:发送端需要等缓冲区满才发送出去,造成粘包。接收方粘包:接收方不及…...
数据泄露态势(2024年5月)
监控说明:以下数据由零零信安0.zone安全开源情报系统提供,该系统监控范围包括约10万个明网、深网、暗网、匿名社交社群威胁源。在进行抽样事件分析时,涉及到我国的数据不会选取任何政府、安全与公共事务的事件进行分析。如遇到影响较大的伪造…...
二手闲置平台小程序的设计
管理员账户功能包括:系统首页,个人中心,用户管理,卖家管理,商品分类管理,商品信息管理,商品购买管理,商品配送管理 微信端账号功能包括:系统首页,商品信息&a…...
协程libgo的使用
c开源协程库libgo介绍及使用-CSDN博客 libgo库的github地址:GitHub - yyzybb537/libgo: Go-style concurrency in C11 使用libgo编写并行程序,即可以像golang一样开发迅速且逻辑简洁,又有C原生的性能优势。它的特点有: 1.提供go…...
什么叫低频晶振?低频晶振最低频率能达到多少?低频晶振封装尺寸有哪些?
低频晶振指的是那些工作在较低频率范围内的晶体振荡器,通常这类振荡器的标称频率低于8MHz。这些晶振在各种电子设备中都有应用,尤其是在那些需要精确但不需要高频振荡的应用场景中,比如实时时钟(RTC)、低速串行通信接口(如UART、IC等)、以及一…...
Splunk Enterprise 任意文件读取漏洞(CVE-2024-36991)
文章目录 前言漏洞描述影响版本漏洞复现POC批量检测-nuclei脚本 修复建议 前言 Splunk Enterprise 是一款强大的机器数据管理和分析平台,能够实时收集、索引、搜索、分析和可视化来自各种数据源的日志和数据,帮助企业提升运营效率、增强安全性和优化业务…...
零基础STM32单片机编程入门(九)IIC总线详解及EEPROM实战含源码视频
文章目录 一.概要二.IIC总线基本概念1.总体特征2.通讯流程 三.EEPROM介绍1.M24C08基本介绍2.向M24C08写一个字节时序图3.从M24C08读一个字节时序图 四.GPIO模拟IIC驱动M24C08读写五.CubeMX工程源代码下载六.讲解视频链接地址七.小结 一.概要 IIC(Inter-Integrated …...
数据库的操作
【一】库的增删改查 【0】导入数据文件 source D:\bjpowernode.sql 【1】创建数据库 语法: create database [if not exists] 数据库名 [character set 编码字符集]; create databases db1; # 设置库的默认编码 create databases db1 charsetgbk; 【2】查看数据…...
常见的认证方式
认证机制是一种用户确定用户身份或者权限的安全措施,比如用来验证某个用户是否有权限访问一个资源,如果认证通过,用户就可以成功访问,反之则会访问失败 常见的认证方式有四种,分别是 Basic、Digest、OAuth 和 Bearer …...
DolphinScheduler部署安装or基础介绍(一)
DolphinScheduler概述 Apache DolphinScheduler是一个分布式、易扩展的可视化DAG工作流任务调度平台。致力于解决数据处理流程中错综复杂的依赖关系,使调度系统在数据处理流程中开箱即用 DolphinScheduler核心架构 DolphinScheduler的主要角色如下: Ma…...
Failed building wheel for pyaudio Running setup.py clean for pyaudio
从错误信息来看,问题出在 pyaudio 包的构建过程中。具体来说,缺少 portaudio.h 头文件,这通常是因为系统上没有安装 portaudio 库。 以下是解决此问题的步骤: 安装系统依赖: 在大多数基于 Debian 的系统(如…...
【ARMv8/v9 GIC- 700 系列 1 -- Programmers model for GIC-700】
请阅读【ARM GICv3/v4 实战学习 】 文章目录 GIC-700 Programmers model寄存器实现和访问模式接口宽度和数据格式访问类型安全寄存器访问地址映射和页面GIC-700 Register map pagesSummaryGIC-700 Programmers model GIC-700是ARM的一种通用中断控制器,它遵循GICv3和GICv4架构…...
exel带单位求和,统计元素个数
如果exel表格中,如果数据有单位,无法直接用 自动求和 直接求和。如下图所示,求和结果为0,显然不是我们想要的。 用下面的公式求和,单位不是“个”的时候记得替换单位。统计范围不是“C1:C7”也记得换一下啊!…...
JavaScript里方括号[]的使用
我们知道可用方括号来表示数组或者JSON对象的属性值,其实在特定场合,方括号还有妙用的。 比如我有数据源是一组JSON,其中有一个属性是时间字符串,我想对时间的小时、星期、日、月分别进行处理。每条JSON都各自生成一条新的JSON&am…...
俯卧撑计数器(Python)
通过 MediaPipe 检测人体姿态,计算俯卧撑角度和计数,并在图像上进行可视化展示 需要有cv2库和mediapipe库 mediapipe库: MediaPipe是Google开源的机器学习框架,用于构建实时音频、视频和多媒体处理应用程序。它提供了一组预训练的…...
UVA12342 Tax Calculator 题解
题目传送门 题目大意 题目描述 某国所得税计算十分复杂。该国政府指定你制作一个自动计算所得税的程序。以下是该国计算所得税的规则: 所得税免征额为 180000 180000 180000 元。应纳税额中不超过 300000 300000 300000 元的所得额,按 10 % 10\% …...
如何永久保存微信聊天记录?WeChatMsg终极指南让你重获数据掌控权
如何永久保存微信聊天记录?WeChatMsg终极指南让你重获数据掌控权 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trendin…...
LangChain + AgentRun 浏览器沙箱极简集成指南
AgentRun Browser Sandbox 介绍 什么是 Browser Sandbox? Browser Sandbox 是 AgentRun 平台提供的云原生无头浏览器沙箱服务,基于阿里云函数计算(FC)构建。它为智能体提供了一个安全隔离的浏览器执行环境,支持通过标准的 Chrome DevTools Protocol (…...
AI风口来袭!转型LLM应用开发工程师,非常详细收藏我这一篇就够了
一、引言:AI时代下的新职业机遇 近年来,随着人工智能技术的快速发展,尤其是大语言模型(Large Language Models, LLM)的突破,软件行业正在经历深刻变革。以GPT系列模型为代表的技术,使自然语言理…...
实战演练:基于Copaw下载的博客代码,在快马平台上快速构建并部署可访问的全栈应用
今天想和大家分享一个实战经验:如何基于Copaw下载的代码,在InsCode(快马)平台上快速构建并部署一个全栈博客应用。整个过程非常流畅,特别适合想快速验证想法的开发者。 项目背景与需求分析 最近在Copaw上找到一个博客系统的代码骨架&#x…...
Mermaid Live Editor:代码驱动的实时图表协作新范式
Mermaid Live Editor:代码驱动的实时图表协作新范式 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-live-editor …...
抗DDoS设备性能测试方法详解:专业仪表如何精准评估防护能力
摘要抗DDoS设备的防护效果如何,单靠厂商自测数据不可信,需要专业网络安全测试仪表进行第三方验证。本文系统梳理SYN Flood、UDP Flood、HTTP Flood、反射放大、慢速攻击等主流DDoS攻击的测试方法,结合运营商级集采测试标准,详解清…...
SparseMoE实战:从零构建一个高效的稀疏混合专家层
1. 稀疏混合专家层(SparseMoE)入门指南 第一次听说稀疏混合专家层时,我也是一头雾水。这玩意儿听起来像是某种高科技黑箱,但实际上它的核心思想特别接地气——就像我们去医院看病,普通全科医生能处理常见病症ÿ…...
palworld-host-save-fix全攻略:解决幻兽帕鲁存档迁移难题的实战指南
palworld-host-save-fix全攻略:解决幻兽帕鲁存档迁移难题的实战指南 【免费下载链接】palworld-host-save-fix 项目地址: https://gitcode.com/gh_mirrors/pa/palworld-host-save-fix 在幻兽帕鲁的冒险旅程中,更换服务器或迁移平台时的存档丢失问…...
小爱音箱音乐自由播放器:解锁无限听歌体验的完整指南
小爱音箱音乐自由播放器:解锁无限听歌体验的完整指南 【免费下载链接】xiaomusic 使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。 项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic 你是否厌倦了音乐平台的各种限制?是否想…...
Z-Image-Turbo问题解决:手把手教你配置Gradio WebUI并映射本地端口
Z-Image-Turbo问题解决:手把手教你配置Gradio WebUI并映射本地端口 1. 为什么选择Z-Image-Turbo 如果你正在寻找一款既快速又高质量的AI图像生成工具,Z-Image-Turbo绝对值得考虑。这个由阿里通义实验室开源的高效文生图模型,在速度和质量的…...
