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,怎么都搞不好,也没有提示。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构之算法的时间复杂度

1.时间复杂度的定义 在计算机科学中&#xff0c;算法的时间复杂度是一个函数&#xff0c;它定量描述了算法的运行时间。一个算法所花费的时间与其中语句的执行次数成正比列&#xff0c;算法中的基本操作的执行次数&#xff0c;为算法的时间复杂度 例1&#xff1a; 计算Func1…

Linux:ollama大模型部署

目录 Ollama 是一个能在本地机器上轻松构建和运行大型语言模型的轻量级、可扩展框架&#xff0c;适用于多种场景&#xff0c;具有易于使用、资源占用少、可扩展性强等特点。 1.安装下载ollama 2.为 Ollama 创建一个用户 3.为ollama创建服务文件 4.启动ollama服务 5.拉取语…

Java 家庭物联网

家庭物联网系统的代码和说明&#xff0c;包括用户认证、设备控制、数据监控、通知和警报、日志记录以及WebSocket实时更新功能。 ### 项目结构 plaintext home-iot-system ├── backend │ └── src │ └── main │ └── java │ └…

图书馆数据仓库

目录 1.数据仓库的数据来源为业务数据库&#xff08;mysql&#xff09; 初始化脚本 init_book_result.sql 2.通过sqoop将mysql中的业务数据导入到大数据平台&#xff08;hive&#xff09; 导入mysql数据到hive中 3.通过hive进行数据计算和数据分析 形成数据报表 4.再通过sq…

【matlab】智能优化算法——求解目标函数

智能优化算法在求解目标函数方面发挥着重要作用&#xff0c;它通过迭代、筛选等方法来寻找目标函数的最优值&#xff08;极值&#xff09;。以下是关于智能优化算法求解目标函数的详细介绍&#xff1a; 一、智能优化算法概述 智能优化算法是一种搜索算法&#xff0c;旨在通过…

设置单实例Apache HTTP服务器

配置仓库 [rootlocalhost ~]# cd /etc/yum.repos.d/ [rootlocalhost yum.repos.d]# vi rpm.repo仓库代码&#xff1a; [BaseOS] nameBaseOS baseurl/mnt/BaseOS enabled1 gpgcheck0[AppStream] nameAppStream baseurl/mnt/AppStream enabled1 gpgcheck0挂载 [rootlocalhost …

2.4G无线收发芯片 XL2401D,SOP16封装,集成单片机,高性价比

XL2401D 芯片是工作在2.400~2.483GHz世界通用ISM频段&#xff0c;片内集成了九齐 NY8A054E单片机的SOC无线收发芯片。芯片集成射频收发机、频率收生器、晶体振荡器、调制解调器等功能模块&#xff0c;并且支持一对多组网和带ACK的通信模式。发射输出功率、工作频道以及通信数据…

网络基础:IS-IS协议

IS-IS&#xff08;Intermediate System to Intermediate System&#xff09;是一种链路状态路由协议&#xff0c;最初由 ISO&#xff08;International Organization for Standardization&#xff09;为 CLNS&#xff08;Connectionless Network Service&#xff09;网络设计。…

油猴脚本高级应用:拦截与修改网页Fetch请求实战指南

油猴脚本高级应用&#xff1a;拦截与修改网页Fetch请求实战指南 简介&#xff1a; 本文介绍了几个使用油猴&#xff08;Tampermonkey&#xff09;脚本拦截和修改网页 fetch 请求的案例。这些脚本可以在浏览器扩展油猴中运行&#xff0c;用于开发者调试网络请求或自定义页面行…

Vue 前端修改页面标题无需重新打包即可生效

在public文件夹下创建config.js文件 index.html页面修改 其他页面的标题都可以用window.title来引用就可以了&#xff01;

【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【19】认证服务03—分布式下Session共享问题

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【19】分布式下Session共享问题 session原理分布式下session共享问题Session共享问题解决—session复制Session共享问题解决—客户端存储Session共享问题解决—hash一致性Session共享问题…

ASUS/华硕飞行堡垒8 FX506L FX706L系列 原厂win10系统 工厂文件 带F12 ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;Windows10 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…

域名、网页、HTTP概述

目录 域名 概念 域名空间结构 域名注册 网页 概念 网站 主页 域名 HTTP URL URN URI HTML 超链接 发布 HTML HTML的结构 静态网页 特点 动态网页 特点 Web HTTP HTTP方法 GET方法 POST方法 HTTP状态码 生产环境下常见的HTTP状态码 域名 概念 IP地…

基于.NET开源游戏框架MonoGame实现的开源项目合集

前言 今天分享一些基于.NET开源游戏框架MonoGame实现的开源项目合集。 MonoGame项目介绍 MonoGame是一个简单而强大的.NET框架&#xff0c;使用C#编程语言可以创建桌面PC、视频游戏机和移动设备游戏。它已成功用于创建《怒之铁拳4》、《食肉者》、《超凡蜘蛛侠》、《星露谷物…

【跟我学K8S】45天入门到熟练详细学习计划

目录 一、什么是K8S 核心功能 架构组件 使用场景 二、入门到熟练的学习计划 第一周&#xff1a;K8s基础和概念 第二周&#xff1a;核心对象和网络 第三周&#xff1a;进阶使用和管理 第四周&#xff1a;CI/CD集成和监控 第五周&#xff1a;实战模拟和案例分析 第六周…

XPointer 实例

XPointer 实例 1. 引言 XPointer 是一种用于定位 XML 文档中特定部分的语言。它是 XLink 的补充,允许用户在 XML 文档中创建链接,指向文档中的特定元素、属性或文本。XPointer 的强大之处在于其精确的定位能力,使得开发者能够创建更加丰富和动态的 XML 应用。 2. XPointe…

【Spring Boot】spring boot主启动类_内置服务

1、主启动类 1.1 定义与功能 Spring Boot的主启动类是一个特殊的Java类&#xff0c;用于启动Spring Boot应用程序。该类通常使用SpringBootApplication注解进行标注&#xff0c;这个注解是一个复合注解&#xff0c;包含SpringBootConfiguration、EnableAutoConfiguration和Co…

LRU Cache 双向链表以及STL list实现----面试常考

双向链表版本&#xff1a; #include <bits/stdc.h> using namespace std; struct Node{int key, value;Node* prev;Node* next;Node():key(0), value(0), prev(nullptr), next(nullptr){}Node(int k, int v):key(k), value(v), prev(nullptr), next(nullptr){} }; class…

【IT领域新生必看】Java中的对象创建魔法:小白也能掌握的五种方法

文章目录 引言为什么需要创建对象&#xff1f;创建对象的五种常见方式1. 使用 new 关键字示例&#xff1a; 2. 使用反射示例&#xff1a; 3. 使用克隆示例&#xff1a; 4. 使用序列化和反序列化示例&#xff1a; 5. 使用工厂方法示例&#xff1a; 选择合适的对象创建方式总结 引…

Spring容器Bean之XML配置方式

一、首先看applicationContext.xml里的配置项bean 我们采用xml配置文件的方式对bean进行声明和管理&#xff0c;每一个bean标签都代表着需要被创建的对象并通过property标签可以为该类注入其他依赖对象&#xff0c;通过这种方式Spring容器就可以成功知道我们需要创建那些bean实…