Android自定义注解

1、什么是注解

An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

翻译:

注释是元数据的一种形式,可以添加到Java源代码中。可以注释类、方法、变量、参数和包。注释对它们注释的代码的操作没有直接影响。

                                                                                                                                                                                                --by 百度翻译

个人理解注解就是一个标记,可以标记在类、字段、方法等上面,如果我们对这个标记不做任何处理那么其实它不起任何作用

2、元注解

元注解就是注解的注解,主要有四个:@Target、@Retention、@Inherited、@Documented

比如:


Android自定义注解_第1张图片

其中Target表示注解的作用范围取ElementType中的值

Android自定义注解_第2张图片
ElementType表示的作用范围

Retention定义了注解在哪个级别可用,有三个取值:

RetentionPolicy.SOURCE:仅在源码期间有效,在编译期间被丢弃

RetentionPolicy.CLASS:保留到class文件中,也就是说编译期间都是可以对其进行处理的

RetentionPolicy.RUNTIME:保留到运行时,即可以用过反射调用


Android自定义注解_第3张图片
展示注解的级别

如上图所示,定义了三个注解Test1、Test2、Test3其中Test3是SOURCE级别的所以我们在反编译出来的dex文件中看不到Test3,Test1和Test2则可以看到。如果我们继续编写代码在运行期间反射的话那么Test2是可以被反射到的,Test1则找不到。(代码不好贴,可以自行验证)


Inherited表示注解是否可以被继承,意思是如果A被注解@Test1注解,B继承A,如果@Test1被Inherited标记那么classB.getAnnotation可以获取到Test1,如果@Test1没有被Inherited标记那么那么classB.getAnnotation就无法获取到Test1。(代码不好贴,可以自行验证)


Android自定义注解_第4张图片

如上图代码所示,Handler3继承Handler2,Handler2被Test2注解,Test2被Inherited注解,然后运行程序打印log:

annotation: @annotation.test.jyb.com.annotation.Test2(value=101),可以发现通过Handler3我们可以取到Test2注解


Documented表示注解是否会保留到文档中


3、AbstractProcessor

AbstractProcessor只能在java library工程里面用,在Android library里面找不到这个类,定义编译期间注解最核心的是要用到AbstractProcessor用于处理我们的注解,它有四个方法可以让我们重写

 1、 public synchronized void init(ProcessingEnvironment processingEnv)

  2、 public Set getSupportedAnnotationTypes()

  3、 public SourceVersion getSupportedSourceVersion()

  4、 public boolean process(Set annotations, RoundEnvironment roundEnv)

init方法主要是用于初始化和获取相关工具类,Init:初始化工作,获取工具类:

processingEnv.getElementUtils(): element操作相关工具类;

processingEnv.getTypeUtils():type操作相关工具类;

processingEnv.getMessager():打印日志;

processingEnv.getFiler(),生成java文件;

processingEnv.getOptions(), 获取gradle中配置的参数

其中用的比较多的是打印日志和生成java文件

getSupportedAnnotationTypes:配置processor处理的注解类。就是指定当前process处理哪几个注解(我们可能会定义多个注解,用多个process进行处理,这个函数告诉当前process处理哪几个注解)

getSupportedSourceVersion: 设置支持的java版本,一般设置为SourceVersion.latestSupported()

process处理注解的方法,主要是获取注解然后根据注解生成代码


4、定义编译期注解

下面用一个例子来说民如何定义编译期间注解,

第一步定义注解:


Android自定义注解_第5张图片
定义注解

如上图,我们定义了一个注解可以用在类、方法、字段上面,CLASS级别(无法反编译获取),可以被继承

第二步编写process方法

新建java library工程,编写我们的process类继承AbstractProcessor

编写process类

这里我们的注解上也添加了一个@AutoService注解,这个AutoService作用是我们不用新建resources文件夹和配置文件。使用方法就是在当前java library工程中添加Gradle依赖(implementation'com.google.auto.service:auto-service:1.0-rc4')然后在process类上添加注解就行


设置Procerssor能够处理的注解

Android自定义注解_第6张图片
设置能够处理的注解

重写process方法

Android自定义注解_第7张图片
重写process

上面process方法,首先通过roundEnv获取被Test1标记的类或者方法或者字段,如果没有找到那么退出

TypeSpec.Builder用于生成一个类(使用javapoet库帮助我们生成代码)

第三部添加一个单例方法(也是使用javapoet库)

for循环,element就是被注解标记的元素,先判断它是否是类如果是那么添加成员函数

最后createFile生成代码。

Android自定义注解_第8张图片
addInstanceMethod
Android自定义注解_第9张图片
addClassMember
Android自定义注解_第10张图片
createFile

addInstanceMethod,addClassMember都是通过javapoet来生成方法和字段的,createFile生成java文件。最后编译我们可以在

app\build\intermediates\classes\debug\annotation\test\jyb\com中查看,标黑的这个是我们生成类的时候设置的包名,所以它会随着包名的变化而变化


Android自定义注解_第11张图片
这个是自动生成的代码

4、定义运行期间注解

定义运行期间注解比较简单,就是在定义注解的时候将Retention设置为RunTime然后通过反射调用就行,比如我们如果要用运行期间注解实现ButterKnife的话需要以下步骤:

第一步定义注解:

Android自定义注解_第12张图片
定义注解

第二部编写辅助代码

Android自定义注解_第13张图片
通过反射注解调用findViewById方法

第三步使用

使用注解

然后在Activity的Oncreate方法中调用InjectBindHelper.inject(this)即可,因为反射效率比较低所以一般不会用这种方法来用findViewById,这里只是演示;


完整demo:https://github.com/yaozhukuang/component,这个demo是通过注解实现项目组件化方案,所以代码跟文中的可能有不一样

你可能感兴趣的:(Android自定义注解)