一、编译时注解处理:
编译时注解的核心依赖APT(Annotation Processing Tools)实现,原理是添加完注解后,在编译时编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,可以在该方法进行相应的处理。
1、定义自定义注解。新建一个Java library,名为 Annolib,定义一个自定义注解
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int id();
}
复制代码
2、定义注解处理器。再次新建一个Java library,用来定义一个注解处理器,名为Processor:
@AutoService(javax.annotation.processing.Processor.class)
@SupportedAnnotationTypes({"example.mydemo.com.annolib.Person"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class Processor extends AbstractProcessor{
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
.....
}
}
复制代码
- @AutoService(Processor.class),用于自动为 JAVA Processor 生成 META-INF 信息。方便注册注解处理器
- @SupportedAnnotationTypes({}),声明 Processor 处理的注解,注意这是一个数组,表示可以处理多个注解;也可以通过getSupportedAnnotationTypes 方法来处理
- @SupportedSourceVersion(SourceVersion.RELEASE_7),声明支持的源码版本。也可以通过getSupportedSourceVersion方法来处理。
- process() 方法的实现,分为两个步骤: (1)收集 Class 内的所有被自定义注解的类;
(2)生成 .java 源文件。可以使用JavaPoet开源库进行编写,提升效率。
Element的子类有:
-
ExecutableElement: 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。 对应@Target(ElementType.METHOD) @Target(ElementType.CONSTRUCTOR)
-
PackageElement; 表示一个包程序元素。提供对有关包极其成员的信息访问。 对应@Target(ElementType.PACKAGE)
-
TypeElement; 表示一个类或接口程序元素。提供对有关类型极其成员的信息访问。 对应@Target(ElementType.TYPE) 注意:枚举类型是一种类,而注解类型是一种接口。
-
TypeParameterElement; 表示一般类、接口、方法或构造方法元素的类型参数。 对应@Target(ElementType.PARAMETER)
-
VariableElement; 表示一个字段、enum常量、方法或构造方法参数、局部变量或异常参数。 对应@Target(ElementType.LOCAL_VARIABLE)
Processor完整代码:
@AutoService(javax.annotation.processing.Processor.class)
@SupportedAnnotationTypes({"example.mydemo.com.annolib.BindView"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class Processor extends AbstractProcessor{
/**
* 指定支持的Java版本,
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 指定处理器需要处理哪些注解
* @return 存储注解的set
*/
@Override
public Set getSupportedAnnotationTypes() {
Set type = new LinkedHashSet<>();
type.add(BindView.class.getCanonicalName());
return type;
}
/**
* 注解器核心代码,return false是不做任何事情
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
//通过自定义的注解拿到element的集合
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
//遍历所有被注解的程序元素
for (Element element : elements) {
ElementKind kind = element.getKind();
//判断该元素是否为字段
if (kind == ElementKind.FIELD) {
//通过message输出
messager.printMessage(Diagnostic.Kind.NOTE,"field:" + element.toString());
}
}
return true;
}
}
复制代码
build.gradle:
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
//依赖自定义注解
implementation project(':Annolib')
//用于生成 .java 源文件
implementation 'com.squareup:javapoet:1.11.1'
//用于自动为 JAVA Processor 生成 META-INF 信息。
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
复制代码
- https://github.com/square/javapoet 方便生成.java文件
3、app module中使用注解: (1)build.gradle:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
//自定义注解库的依赖
implementation project(':Annolib')
//自定义注解处理器的依赖
annotationProcessor project(':processor')
}
复制代码
annotationProcessor 仅仅在编译期去依赖注解处理器所在库进行工作,不会打包到APK 中。
(2)注解使用:
@BindView(id = R.id.tv_title)
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
复制代码
通过命令 ./gradlew build去构建工程,输出了注解View的Id:
二、Android中常用注解
依赖: 在build.gradle中增加:
implementation 'com.android.support:support-annotations:27.1.1'
复制代码
官方文档:
https://developer.android.com/reference/android/support/annotation/package-summary
1、@NonNull 声明方法的参数不能为空
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String name = null;
initView(name);
}
private void initView(@NonNull String s) {
}
复制代码
2、@Nullable注解表示一个方法的参数或者返回值可以是Null
private void initView(@Nullable String s) {
}
复制代码
3、@StringRes注解表示需要一个String类型的资源id
private void initView(@StringRes int strId) {
}
复制代码
4、@IdRes 表示需要一个资源id
private void initView(@IdRes int id) {
}
复制代码
5、@IntDef 用来替代枚举:
public static final int ONE = 1;
public static final int TWO = 2;
public static final int THREE = 3;
@IntDef({ONE, TWO, THREE})
public @interface Common{
}
public void setValue(@Common int value) {
}
private void initView() {
setValue(ONE);
}
复制代码