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.pswp.cn/web/40526.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40526.shtml
英文地址,請注明出處:http://en.pswp.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實…