什么是元注解:
简单来说,就是注解的注解,元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明:
1. @Target,
2. @Retention,
3. @Documented,
4. @Inherited
元注解的作用和相应分参数的使用说明:
@Target:
该标识用于描述注解的使用范围(即:被描述的注解可以用在什么地方): packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
取值(ElementType)有:
1. CONSTRUCTOR:用于描述构造器
2. FIELD:用于描述域
3. LOCAL_VARIABLE:用于描述局部变量
4. METHOD:用于描述方法
5. PACKAGE:用于描述包
6. PARAMETER:用于描述参数
7. TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention:
该标示定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。
取值(ElementType)有:
1. SOURCE:在源文件中有效(即源文件保留)
2. CLASS:在class文件中有效(即class保留)
3. RUNTIME:在运行时有效(即运行时保留)
@Documented:
Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员
@Inherited:
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
==注意==:
@Inheritedannotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承
annotation,方法并不从它所重载的方法继承annotation。
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,
则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited
annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指
定的annotation类型被发现,或者到达类继承结构的顶层。
自定义注解:
定义注解格式:
public @interface 注解名 {定义体}
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
注解参数的可支持数据类型:
1. 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2. String类型
3. Class类型
4. enum类型
5. Annotation类型
6. 以上所有类型的数组
Annotation类型里面的参数设定:
1. 只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;
2. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和
String,Enum,Class,annotations等数据类型,以及这一些
类型的数组.例如,String value();这里的参数成员就为String;
3. 如果只有一个参数成员,最好把参数名称设为”value”,
后加小括号.例:下面的例子FruitName注解就只有一个参数成员。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnimalName {
String value() default "";
}
APT的介绍:
APT英文全称:Android annotation process tool是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成源文件和原来的源文件,将它们一起生成class文件。简言之:APT可以把注解,在编译时生成代码。
APT的处理要素:
注解处理器(AbstractProcess)+代码处理(javaPoet)+处理器注册(AutoService)+apt
使用APT来处理annotation的流程:
1. 定义注解(如@automain)
2. 定义注解处理器
3. 在处理器里面完成处理方式,通常是生成java代码。
4. 注册处理器
5. 利用APT完成如下图的工作内容。
- 定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface DIActivity {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DIView {
int value() default 0;
}
- 定义注解处理器
在定义注解处理器的时候还需要对注解器进行配置,其位置为:
resources
- META-INF
- services
- javax.annotation.processing.Processor
Processor内容:
com.soubu.di.DIProcessor # 指定处理器全类名
==但了更方便的增加这个配置,我们只需要在,AnLogProcessor类中,加一个注解 @AutoService(Processor.class) 编译器会自动的为我们生成这个配置文件了。==
package com.soubu.di;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
@AutoService(Processor.class)
public class DIProcessor extends AbstractProcessor {
private Elements elementUtils;
@Override
public Set<String> getSupportedAnnotationTypes() {
// 规定需要处理的注解
return Collections.singleton(DIActivity.class.getCanonicalName());
}
/**
* public final class DIMainActivity extends MainActivity {
* public static void bindView(MainActivity activity) {
* activity.tv = (android.widget.TextView) activity.findViewById(R.id.tv);
* }
* }
*
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(DIActivity.class);
for (Element element : elements) {
// 判断是否Class
TypeElement typeElement = (TypeElement) element;
List<? extends Element> members = elementUtils.getAllMembers(typeElement);
MethodSpec.Builder bindViewMethodSpecBuilder = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.VOID)
.addParameter(ClassName.get(typeElement.asType()), "activity");
for (Element item : members) {
DIView diView = item.getAnnotation(DIView.class);
if (diView == null) {
continue;
}
bindViewMethodSpecBuilder.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)", item.getSimpleName(), ClassName.get(item.asType()).toString(), diView.value()));
}
TypeSpec typeSpec = TypeSpec.classBuilder("DI" + element.getSimpleName())
.superclass(TypeName.get(typeElement.asType()))
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(bindViewMethodSpecBuilder.build())
.build();
JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
private String getPackageName(TypeElement typeElement) {
return elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
}
继承了AbstractProcessor抽象类,并重写以下五个方法:
1. init()
首先 init() 方法完成sdk版本的判断以及相关帮助类的初始化,帮助类主要有以下几个:
Elements elementUtils,注解处理器运行扫描源文件时,以获取元素(Element)相关的信息。
Element 有以下几个子类:
- 包(PackageElement)、类(TypeElement)、成员变量(VariableElement)、方法(ExecutableElement)
- Types typeUtils,
- Filer filer,用来生成 java 类文件。
- Trees trees,
2. getSupportedAnnotationTypes()
该方法返回一个Set,代表ButterKnifeProcessor要处理的注解类的名称集合,即 ButterKnife 支持的注解:butterknife-annotations
3.getSupportedSourceVersion()
返回当前系统支持的 java 版本。
4. getSupportedOptions()
返回注解处理器可处理的注解操作。
5. process()
在这里完成了目标类信息的收集并生成对应 java 类。
到此,这里的DIAptActivity是我们自动生成的,这里就需要使用到JavaPoet,要使用的话,必须先编译一下项目,生成java源代码。
JavaPoet 中一些重要的类(这些类还有许多实用的方法哦):
- TypeSpec 表示类、接口、或者枚举声明
- ParameterSpec 表示参数声明
- MethodSpec 表示构造函数、方法声明
- FieldSpec 表示成员变量,一个字段声明
- CodeBlock 表示代码块,用来拼接代码
- JavaFile 表示Java类的代码
还有几个占位符也了解下:
- L,forLiterals替换字符串、原语、JavaPoet中的类型例如beginControlFlow(“for(inti= L , f o r L i t e r a l s 替 换 字 符 串 、 原 语 、 J a v a P o e t 中 的 类 型 例 如 b e g i n C o n t r o l F l o w ( “ f o r ( i n t i = L; i < L;i++)”,1,10)addStatement(“result=result L ; i + + ) ” , 1 , 10 ) a d d S t a t e m e n t ( “ r e s u l t = r e s u l t L i”, “+”)
- S,forStrings替换字符串,例如addStatement("return S , f o r S t r i n g s 替 换 字 符 串 , 例 如 a d d S t a t e m e n t ( " r e t u r n S”, “hello”)
- T,forTypes替换类型,例如addStatement("returnnew T , f o r T y p e s 替 换 类 型 , 例 如 a d d S t a t e m e n t ( " r e t u r n n e w T()”, Date.class)
- $N,for Names 替换JavaPoet中的声明
在app的Activity中使用:
@DIActivity
public class AptActivity extends AppCompatActivity {
@DIView(R.id.tv)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DIAptActivity.bindView(this);
tv.setText("ReflectActivity");
}
}
引用相关文章:
https://blog.csdn.net/fengxingzhe001/article/details/78520298
https://www.jianshu.com/p/1910762593be
https://www.jianshu.com/p/94979c056b20
https://www.jianshu.com/p/dce26aa75060
https://www.jianshu.com/p/39fc66aa3297