【Android】APT

news2024/9/22 3:41:34

引言:

安卓中APT又叫Annotation Processing Tool,即注解处理器。Java中注解分为编译时注解和运行时注解,编译时注解无法通过反射的方式进行获取和处理,所以我们可以利用APT处理编译时注解。比如常见的三方库ButterKnife、ARouter均有使用到这种技术。通过注解处理器在编译时期处理注解,并动态的生成java类文件。

实现:

1、新建一个Android工程,在工程中创建两个Java/Kotlin lib,本项目采用Kotlin实现。

在这里插入图片描述
一个lib用来定义注解,另外一个定义processor。由于我们通常注解和处理器需要分开使用,所以分开定义。

2、注解的定义:

package com.example.annotation

import kotlin.reflect.KClass

@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class Register(val service: KClass<*>)

3、注解处理器的定义:

  1. 注解处理器需要创建java/kotlin lib进行编写的原因时,我们需要继承自AbstractProcessor(存在于jdk中,android sdk没有)
  2. 在注解处理器中,我们将获取被指定注解修饰的类(类A),并且根据这个类生成一个代理类,这个代理类实现了我们定义的接口,并且重写的方法将返回一个类A的对象。需要添加依赖:
    implementation project(":annotation") //需要使用到注解
    implementation "com.squareup:javapoet:1.13.0" //一个可以进行代码生成的库
  1. 我们还需要创建一个文件,指向我们自定义的注解处理器,这样在编译时,javac才会使用我们自定义的处理器处理注解。
    创建如下目录并定义如下文件:
    在这里插入图片描述
    如果不想这样做的话,也可以直接使用三方库,具体使用方式这里不展开叙述:
//用于自动为 JAVA Processor 生成 META-INF 信息。
    implementation 'com.google.auto.service:auto-service:1.0-rc3'

注解处理器的具体实现:

package com.example.processor

import com.example.annotation.Register
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeSpec
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Filer
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.util.ElementFilter

class ServiceProcessor: AbstractProcessor() {

    private var filer: Filer? = null
    private var interfaceClassName: ClassName = ClassName.get("com.example.apttest", "TestInterface") //我们的代理类将继承自这个接口

    override fun init(processingEnv: ProcessingEnvironment?) {
        super.init(processingEnv)
        filer = processingEnv?.filer
    }

    override fun process(set: MutableSet<out TypeElement>?, roundEnvironmet: RoundEnvironment?): Boolean {
        roundEnvironmet?.let {
            val elements = it.getElementsAnnotatedWith(Register::class.java) //获取到所有被注解的元素
            val typeElements = ElementFilter.typesIn(elements) //获取到TypeElement类型
            for (typeElement in typeElements) {
                val qualifiedName = typeElement.qualifiedName.toString() //类的完整名称
                val className = qualifiedName.substring(qualifiedName.lastIndexOf(".") + 1) + "\$\$Binder"
                val packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."))
                val presentClassName = ClassName.bestGuess(qualifiedName)
                val builder = TypeSpec.classBuilder(className) //创建一个类文件
                    .addJavadoc("自动生成的类") //添加注释
                    .addModifiers(Modifier.FINAL, Modifier.PUBLIC) //声明访问权限
                    .addSuperinterface(ParameterizedTypeName.get(interfaceClassName, presentClassName)) //设置实现接口,第二个参数为泛型类型

                val method = MethodSpec.methodBuilder("getInterface") //创建一个方法
                    .addAnnotation(Override::class.java)
                    .addModifiers(Modifier.PUBLIC)
                    .returns(presentClassName) //返回类型
                    .addStatement("return new \$T()", ClassName.get(typeElement)) //方法体

                builder.addMethod(method.build())
                try {
                    val javaFile = JavaFile.builder(packageName, builder.build()).build()
                    javaFile.writeTo(filer)
                    println("lqs: writeTo")
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }
        return true
    }

    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.latestSupported()
    }

	// 重写这个方法加入我们需要处理的注解类型
    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        val set: HashSet<String> = HashSet()
        set.add(Register::class.java.canonicalName)
        return set
    }
}

4、注解处理器的调用:

  1. 首先,我们要去添加注解,在app模块创建如下文件:
    在这里插入图片描述
    TestInterface即我们代理类需要实现的接口
package com.example.apttest

interface TestInterface<T> {
    fun getInterface(): T
}

TestClass将会被我们定义的注解所修饰

package com.example.apttest

import android.content.Context
import android.widget.Toast
import com.example.annotation.Register

@Register(service = MainActivity::class)
class TestClass {
    fun startService(context: Context) {
        Toast.makeText(context, "hello", Toast.LENGTH_LONG).show()
    }
}
  1. 我们还需要一个工具类来调用我们生成的代理类以获取一个类A的对象
package com.example.apttest

object TestUtil {

    fun <T> getService(apiClazz: Class<T>): T? {
        val className = apiClazz.canonicalName?.plus("\$\$Binder") ?: ""
        var instance = Any()
        try {
            val clazz = Class.forName(className)
            instance = clazz.newInstance()
            if (instance is TestInterface<*>) {
                val target = instance.getInterface()
                if (target != null) {
                    instance = target
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return instance as T?
    }
}
  1. 在Activity中尝试调用:
package com.example.apttest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        TestUtil.getService(TestClass::class.java)?.startService(this)
    }
}
  1. 此时其实注解处理器并没有开始工作,还需要在build.gradle中进行注解处理器的配置:
    kotlin支持使用kapt进行配置,java参考annotationProcessor
plugins {
    id 'kotlin-kapt'
}
dependencies {
	kapt project(":processor")
}

5、编译结果

我们生成的类在build目录下即可找到:
在这里插入图片描述
至此,我们通过注解处理器在编译阶段(build阶段)获取到了被注解修饰的类(类A),并生成了一个代理类实现指定接口返回了一个类A的对象,实现对类A中方法的调用。

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

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

相关文章

mysql删除重复记录并且只保留一条

准备的测试表结构及数据 插入的数据中A,B,E存在重复数据,C没有重复记录 CREATE TABLE tab ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(20) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT13 DEFAULT CHARSETutf8; -- --------------------…

使用root用户和普通用户完成分配任务案例 ansible(1)

目录 案例一&#xff1a; 控制主机和受控主机通过root用户通过免密验证方式远程控制受控主机实施对应任务。 案例二&#xff1a; 控制主机连接受控主机通过普通用户以免密验证远程控制受控主机实施特权指定操作。 案例一&#xff1a; 控制主机和受控主机通过root用户通过免…

<网络概述>——《计算机网络》

目录 1.网络基础 1.1 计算机网络背景 1.2 网络发展 1.3 软件分层 1.4 网络和操作系统的关系 1.5 局域网通信的原理 2. 网络协议 2.1 网络协议初识 2.1.1 协议分层 2.2 OSI七层模型 2.3 TCP/IP五层(或四层)模型 3. 网络传输基本流程 3.1 网络传输流程图 3.2 数据包…

密码技术扫盲,Part 3:认证

个人博客 密码技术扫盲&#xff0c;Part 1&#xff1a;对称加密密码技术扫盲&#xff0c;Part 2&#xff1a;非对称加密&#x1f3af; 密码技术扫盲&#xff0c;Part 3&#xff1a;认证 除了加密&#xff0c;还有一类用法是对信息的认证&#xff0c;主要包括 4 个技术 单向散…

PostgreSQL JIT 实现query性能加速的一些补充

文章目录背景Executor 本身做的一些优化LLVM JIT 的优化本地以及全局优化执行 query 时的优化JIT调度优化 过程背景 之前介绍过一次 PostgreSQL JIT with LLVM 实现&#xff0c;因为有一些细节没有介绍得很清楚&#xff0c;需要额外做一些补充。 关于LLVM 的IR 以及 如何 用LL…

11.HTML颜色、HTML脚本、字符实体、URL

1.HTML颜色 1&#xff09;HTML 颜色由红色、绿色、蓝色混合而成。 2&#xff09;HTML 颜色由一个十六进制符号来定义&#xff0c;这个符号由红色、绿色和蓝色的值组成&#xff08;RGB&#xff09;。 3&#xff09;每种颜色的最小值是0&#xff08;十六进制&#xff1a;#00&…

性能优化系列之怎么让图片加载得更快?

文章の目录一、压缩png1、优势2、说明文档3、安装4、使用4.1、环境4.2、示例4.3、API4.4、还有一个第三库是对当前库的封装&#xff0c;叫jdf-png-native【版本1.1.0&#xff0c;环境同node-pngquant-native】&#xff0c;使用方法和node-pngquant-native差不多二、压缩jpg1、优…

分享68个PHP源码,总有一款适合您

链接&#xff1a;https://pan.baidu.com/s/1QB61EsIl70vXx1yrJkiesA?pwdbup1 提取码&#xff1a;bup1 PHP源码 分享68个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载…

【树莓派不吃灰】IO篇① GPIO 开发环境

目录1. 前言1.1 4B GPIO引脚排列2. Python GPIO安装3. 基于C语言的wiringPi安装❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-01-01 ❤️❤️ 本篇更新记录 2023-01-01 ❤️&#x1f389; 欢迎关注 &#x1f50e;点赞 &…

vivo 服务端监控体系建设实践

作者&#xff1a;vivo 互联网服务器团队- Chen Ningning 本文根据“2022 vivo开发者大会"现场演讲内容整理而成。 经过几年的平台建设&#xff0c;vivo监控平台产品矩阵日趋完善&#xff0c;在vivo终端庞大的用户群体下&#xff0c;承载业务运行的服务数量众多&#xff0c…

Modbus-RTU通讯协议中CRC校验

MODBUS协议中的CRC校验_RobotWoods的博客-CSDN博客_modbus crc 以下面这段收发数据为例&#xff1a; 发送的数据是工控机发送给plc的&#xff0c;他们的通信是modbus通信&#xff0c;前面的01。。。。。0f是实际使用的数据&#xff0c;具体意义可以查&#xff0c;后面的4位数87…

对模板类型推演的一个小Bug

在写vector的代码时遇到了这样一个错误 以下是代码书写内容以及我的测试代码 测试代码的目的就是想测试一下我写的vector构造方法 vector(size_t n, const T& val T())&#xff1b; 可是却发生了上述的报错 vector(size_t n, const T& val T()){// 法一&#xff1a…

【云原生】springcloud13——Config分布式配置中心

前 言 &#x1f349; 作者简介&#xff1a;半旧518&#xff0c;长跑型选手&#xff0c;立志坚持写10年博客&#xff0c;专注于java后端 ☕专栏简介&#xff1a;深入、全面、系统的介绍springcloud与springcloud Alibaba微服务常用技术栈 &#x1f330; 文章简介&#xff1a;本文…

JUC并发编程学习笔记(三)集合线程安全及多线程锁

4 集合的线程安全 4.1 集合操作 Demo 创建集合使用string的泛型 for (int i 0; i <30; i) {new Thread(()->{//向集合添加内容list.add(UUID.randomUUID().toString().substring(0,8));//从集合获取内容System.out.println(list);},String.valueOf(i)).start(); }查看…

Odoo 16 企业版手册 - 库存管理之产品变体与属性

产品变体和属性 向客户提供多种产品变体是提高销售的有效策略。Odoo允许您在库存模块中配置产品的不同变体&#xff0c;就像我们在销售和购买模块中所做的那样。首先&#xff0c;确保从库存模块的「设置」菜单中激活「变体」功能&#xff0c;如下所示。 「产品变体」选项可以从…

【MySQL基础教程】事务详细介绍

前言 本文为 【MySQL基础教程】事务 相关知识&#xff0c;下边将对事务简介&#xff0c;事务操作&#xff08;包括&#xff1a;未控制事务&#xff0c;控制事务&#xff09;&#xff0c;事务四大特性&#xff0c;并发事务问题&#xff0c;事务隔离级别等进行详尽介绍~ &#x…

Linux安装mongodb社区版

1、下载资源 Download MongoDB Community Server | MongoDB 2、将下载的安装包上传到服务器上&#xff0c;并解压缩 #解压 tar -zxvf mongodb-linux-x86_64-rhel70-6.0.3.tgz #移动路径重命名mv mongodb-linux-x86_64-rhel70-6.0.3 /usr/local/mongodb 3、进入 /usr/local/…

适合中学生看的英文电影

怎样利用好丰富的资源来学习英语口语呢&#xff1f;其实其实看什么样的剧、如何看剧都是很讲究的。一起来解锁吧。 一、选剧要学会拆解自己学习目标&#xff0c;选定合适的类型&#xff0c;各取所需。 并不是所有类型的国外影视剧都适合作为学习的素材&#xff0c;主要依据自身…

Python Pyecharts柱状横向动态图-全球GDP比较

程序示例精选 Python Pyecharts柱状横向动态图 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<Python Pyecharts柱状横向动态图>>编写代码&#xff0c;主要功能包括了数据导入&am…

新年新气象----2022年度总结

年度总计——2.0 今年的时间很快飞一般的速度&#xff0c;回顾自己这一年中的日子&#xff0c;不知不觉就结束了&#xff0c;今年确确实实有太多的遗憾&#xff0c;太多的失落和不如意。也让我明白要是这样&#xff0c;就不会那样的道理。 回顾自己去年所定的目标&#xff0c;不…