最近在学习安卓开源框架发现,很多的开源框架都使用到了注解处理器,例如EventBus3.0。本文通过一个简单的Demo来介绍如何使用注解处理器。Demo链接为https://github.com/cugyong/AnnotationProcessor-sample ,如果喜欢的话,欢迎大家给star。

Demo需求描述

用户通过执行一个传入参数为A(类对象)的静态方法,该方法会最终把参数A中加了特定注解的所有方法执行一遍。

需求实现

整个项目分为四个部分:

  • 注解–要使用的注解类型,这部分通常也可以放在lib中;
  • 注解处理器–要对注解进行处理的逻辑,包括收集有特定注解类型的方法信息以及生成特定的java文件;
  • lib–封装合适的接口,供具体调用方调用;
  • sample–具体的调用方逻辑。

首先新建一个安卓工程,点击运行展示的是hello world。

注解

在上述工程中new->Module->Java Library,新建一个Java Library Module,命名为annotation。在该Module下创建一个文件AnnotationTest.java,

AnnotationTest.java里面代码如下:

1
2
3
4
5
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface AnnotationTest {
String name() default "test";
}

1、注解@Retention按生命周期来划分可分为3类:

  • RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
  • RetentionPolicy.CLASS:注解被保留到class文件,当jvm加载class文件时候被遗弃,这是默认的生命周期;
  • RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。

这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。

2、注解@Target表示修饰的注解能使用的范围,ElementType.METHOD表示@AnnotationTest注解只能作用在方法上。

注解处理器

参照上部分,在工程中new->Module->Java Library,新建一个Java Library Module, 在该Module下创建一个文件ProcessorTest.java。在该Module下的build.gradle的dependencies中添加如下配置:

1
2
3
4
// 自动为processor注册
implementation 'com.google.auto.service:auto-service:1.0-rc2'
// 该Module依赖上部分建立的annotation Module
implementation project(':annotation')

com.google.auto.service:auto-service:1.0-rc2依赖的作用是为注解处理器自动注册,它会生成META-INF文件夹。

注解处理器ProcessorTest的定义如下,其中@AutoService(Processor.class)就是build.gradle中加的依赖帮助其自动注册。

1
2
@AutoService(Processor.class) // 自动为ProcessorTest注册,生成META-INF文件
public class ProcessorTest extends AbstractProcessor{

注解处理器ProcessorTest主要包含以下几个部分:

1
2
3
4
5
6
7
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment); mMessager = processingEnvironment.getMessager();
mFiler = processingEnvironment.getFiler();
}

init方法是注解处理器会自动调用的初始化方法,其中mFiler是用来生成java源文件的工具,mMessager是用来打印日志的,它们的具体使用会在后面介绍。

1
2
3
4
5
6
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> supportAnnotationTypes = new HashSet<>();
supportAnnotationTypes.add(AnnotationTest.class.getCanonicalName());
return supportAnnotationTypes;
}

getSupportedAnnotatio 大专栏  安卓注解处理器-processornTypes()方法返回该注解处理器支持的注解类型,这里返回的就是我们之前声明的新的注解类型@AnnotationTest。

1
2
3
4
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}

getSupportedSourceVersion()方法一般就按照上述实现就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
// 打印日志
mMessager.printMessage(Diagnostic.Kind.NOTE, "process start");
Map<String, List<String>> collectInfos = new HashMap<>();
for (TypeElement annotation: annotations){
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation);
for (Element element: elements){
// 检查element是否符合我们定义的规范
if (!checkValid(element)){
mMessager.printMessage(Diagnostic.Kind.NOTE, "checkValid not pass");
return false;
}else {
ExecutableElement executableElement = (ExecutableElement) element;
// 获取被注解的方法所在的类
TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();
// 获取类的全名,包括包名
String classFullName = typeElement.getQualifiedName().toString();
// 被注解的方法的名字
String methodName = executableElement.getSimpleName().toString();
List<String> methods = collectInfos.get(classFullName);
if (methods == null){
methods = new ArrayList<>();
collectInfos.put(classFullName, methods);
}
methods.add(methodName);
}
}
} for (Map.Entry<String, List<String>> entry: collectInfos.entrySet()){
mMessager.printMessage(Diagnostic.Kind.NOTE, entry.getKey());
// 生成java源文件
createJavaFile(entry.getKey(), entry.getValue());
} return true;
}

process方法是我们的主要逻辑处理的地方,主要逻辑就是收集所有有@AnnotationTest注解的方法以及其所在的类信息,然后根据每个类信息,生成一个新的类文件,并在新的类文件的特定方法中调用所有关联的注解方法。生成java源文件将使用Filer对象,具体如何使用请下载demo看源代码。

注:

1、当你点击buid project时,注解处理器将会执行,而Messager对象打印出来的日志信息可以在Gradle Console窗口中看到。

2、如果你在该Module中使用中文注解,因为该Module为java library,可能会报GBK编码错误,解决办法是在该Module的build.gradle中添加如下代码:

1
2
3
4
//指定编译的编码
tasks.withType(JavaCompile){
options.encoding = "UTF-8"
}

Lib

在工程中new->Module->Android Library ,新建一个Android Library Module,封装接口给调用方使用,具体逻辑请参考demo。

最终该demo的功能是点击Hello world文字,会依此执行MainActivity中使用@AnnotationTest注解的方法。

安卓注解处理器-processor的更多相关文章

  1. AS 注解处理器 APT Processor MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. 自定义jsr-269注解处理器 Error:服务配置文件不正确,或构造处理程序对象javax.annotation.processing.Processor: Provider not found

    出现的原因 自定义处理器还没有被编译就被调用,所以报 not found在根据配置寻找自定义的注解处理器时,自定义处理器还未被编译12解决方式 maven项目可以配置编译插件,在编译项目之前先编译处理 ...

  3. java 命名代码检查-注解处理器

    命名代码检查 根据 <Java 语言规范( 第 3 版 ) > 中第6.8节的要求, Java 程序命名应当符合下列格式的书写规范: 类 ( 或接口 ) : 符合驼式命名法, 首字母大写. ...

  4. Java注解处理器(转)

    Java中的注解(Annotation)是一个很神奇的东西,特别现在有很多Android库都是使用注解的方式来实现的.一直想详细了解一下其中的原理.很有幸阅读到一篇详细解释编写注解处理器的文章.本文的 ...

  5. Java注解处理器使用详解

    在这篇文章中,我将阐述怎样写一个注解处理器(Annotation Processor).在这篇教程中,首先,我将向您解释什么是注解器,你可以利用这个强大的工具做什么以及不能做什么:然后,我将一步一步实 ...

  6. Java注解(3)-注解处理器(编译期|RetentionPolicy.SOURCE)

    注解的处理除了可以在运行时通过反射机制处理外,还可以在编译期进行处理.在编译期处理注解时,会处理到不再产生新的源文件为止,之后再对所有源文件进行编译. Java5中提供了apt工具来进行编译期的注解处 ...

  7. Java注解处理器--编译时处理的注解

    1. 一些基本概念 在开始之前,我们需要声明一件重要的事情是:我们不是在讨论在运行时通过反射机制运行处理的注解,而是在讨论在编译时处理的注解.注解处理器是 javac 自带的一个工具,用来在编译时期扫 ...

  8. 基于注解处理器开发自动生成getter和setter方法的插件

    昨天无意中,逛到了lombok的网站,并看到了首页的5分钟视频,视频中的作者只是在实体类中写了几个字段,就可以自动编译为含setter.getter.toString()等方法的class文件.看着挺 ...

  9. Java注解与自己定义注解处理器

    动机 近期在看ButterKnife源代码的时候.竟然发现有一个类叫做AbstractProcessor,并且ButterKnife的View绑定不是依靠反射来实现的,而是使用了编译时的注解,自己主动 ...

随机推荐

  1. iOS个人中心渐变动画、微信对话框、标签选择器、自定义导航栏、短信验证输入框等源码

    iOS精选源码 简单的个人中心页面-自定义导航栏并予以渐变动画 程序员取悦女票的正确姿势---Tip1(iOS美容篇) iOS 前台重启应用和清除角标的问题 微信原生提醒对话框3.0 JHLikeBu ...

  2. PAT甲级——1152.Google Recruitment (20分)

    1152 Google Recruitment (20分) In July 2004, Google posted on a giant billboard along Highway 101 in ...

  3. 爬虫笔记(一)——快速使用urllib库

    本人以前用的都是python2.7,但看网上很多教程都是以python3为例的,所以便切换版本,导入urllib.lxml.beautifulsoup4等库. 下面介绍下两个版本对urllib库的区别 ...

  4. set_include_path详细解释(转)

    首先我们来看这个全局变量:__FILE__它表示文件的完整路径(当然包括文件名在内)也就是说它根据你文件所在的目录不同,有着不同的值:当然,当它用在包行文件中的时候,它的值是包含的路径: 然后:我们看 ...

  5. C2. Power Transmission (Hard Edition)(线段相交)

    This problem is same as the previous one, but has larger constraints. It was a Sunday morning when t ...

  6. PRaCtice[1]

    目录 ID-3学习 代码实现 1. 样例数据集 2.信息熵的计算 ID-3学习 代码实现 该项目采用了业界领先的 TDD(TreeNewBee-Driven Development,吹牛逼导向开发模式 ...

  7. Rails Create--params说明

    参考:https://ruby-china.github.io/rails-guides/getting_started.html 表单提交后,其字段以参数形式传递给 Rails,然后就可以在控制器动 ...

  8. mysql之存储过程(一)

    今天开发一个需求,需要在一个旧表中增加一列并且对已经的表中记录初始化新列的值, 由于是一次性的工作,故写了个存储过程来代替代码程序初始化 创建及执行过程记录如下: MySQL [XXX_YYY]> ...

  9. Momentum(动量)方法的python实现

    Momentum方法可以说是对SGD的进一步优化,细节可以参考这里 这里用python对其进行简单实现,如下: # coding=utf-8 """ 基于小批量梯度下降来 ...

  10. 谁能率先挖掘出5G金矿?

    现在,消费者已经习惯4G的存在,它好像另外的一个太阳,点亮了夜生活,也丰富了白天的生活:随时随地的直播.视频通话.移动支付.嘀嘀打车等等,这些都因4G网络和智能手机而快速发展,帮助消费者清理碎片时间之 ...