一、注解
在类,接口,变量,方法前面的声明,注解的作用是对这些元素进行注释说明,不会影响编译器,通过编译器读取注解内容,如在编译时实现格式检查操作,运行时读取注解跟踪代码。
定义注解,类似定义接口,在interface前加上一个@符号,@interface。
@Target(ElementType.METHOD)//作用范围是方法
@Retention(RetentionPolicy.SOURCE)//源码级注解
public @interface Override {
}
Java Sdk定义的一个注解Override,它是Java中常用注解,作用范围是方法,表示该方法继承父类,仅在源码中保留。当编译器编译该Java源文件时,检查到注解,通知编译器这是一个继承父类的方法,编译器会检查父类是否有该方法,如果没有会编译报错:方法不会覆盖或实现超类型的方法。
@Target和@Retention是元注解,即作用于其他注解的注解。
元注解@Target,注解的使用范围,即注解可以在哪里使用。
FIELD ,字段
TYPE,类,接口
METHOD,方法
CONSTRUCTOR,构造方法
PARAMETER,方法参数
TYPE,表示注解声明在接口和类的前面,如果把一个@Target=TYPE的注解声明在某个方法或变量上,IDE会报错,其他类型也是一样。
元注解@Retention,注解生命周期,即该保留到哪一步。
SOURCE,仅在Java源码文件中保留,编译成class字节码后被抛弃。
CLASS,在class字节码文件中保留,在字节码处理时有用,类加载时忽略。
RUNTIME,在class字节码文件中保留,第一次加载时读取,运行时一直保留,反射获取。
只有注解生命周期@Retention=RUNTIME时,在运行时才能获取定义的注解,通过getAnnotation()方法。
元注解@Inherited,一个类声明了注解,当该注解有@Inherited时,该类的子类可以继承这个注解,即使子类没有声明该注解。
元注解@Documented,是否保存Javadoc文档。
二、自定义注解
定义一个类注解AnnotationClass,作用范围是类,运行时。
@Target(TYPE)//作用范围是类
@Retention(RUNTIME)//运行时注解
public @interface AnnotationClass {
String authorName();
String createTime();
}
定义一个变量注解AnnotationField,作用范围是变量,运行时。
@Documented
@Target({ FIELD, })//作用范围是变量
@Retention(RUNTIME)//运行时注解
public @interface AnnotationField {
String descInfo() default "默认:我是Filed注解";
}
定义一个方法注解AnnotationMethod,作用范围是方法,运行时。
@Target(METHOD)//作用范围是方法
@Retention(RUNTIME)//运行时注解,
public @interface AnnotationMethod {
String name() default "";
}
在类中使用注解。
//注解类
@AnnotationClass(authorName = "chen", createTime = "2019年")
public class MainActivity extends Activity {
//注解变量
@AnnotationField(descInfo = "成功信息")
String mSuccessMsg;
//注解方法
@AnnotationMethod(name = "initView方法")
private void initView(Context context) {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//运行时注解打印,包括方法注解、类注解和变量注解
//只有运行时注解才能获取到
try {
Method method = MainActivity.class.getDeclaredMethod("initView", Context.class);
AnnotationMethod appAnnot = method.getAnnotation(AnnotationMethod.class);
if (appAnnot != null) {
Log.d(TAG, "method:" + method.getName() + ",name:" + appAnnot.name());
}
} catch (NoSuchMethodException e) {
}
try {
Field field = this.getClass().getDeclaredField("mSuccessMsg");
AnnotationField appAnnot = field.getAnnotation(AnnotationField.class);
if (appAnnot != null) {
Log.d(TAG, "field:" + field.getName() + ",mSuccessMsg:" + appAnnot.descInfo());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
AnnotationClass annotationClass = MainActivity.class.getAnnotation(AnnotationClass.class);
if (annotationClass != null) {
Log.d(TAG, "createTime:" + annotationClass.createTime() + ",author:" + annotationClass.authorName());
}
}
}
注解类,作者和创建时间,Class类getAnnotation方法获取AnnotationClass注解对象,具体对象是动态代理对象$Proxy2。
注解方法,反射,根据方法名获取Method,Method的getAnnotation()方法,获取AnnotationMethod,读取注解对象的name字段。
注解变量,反射,根据变量名获取Field,Field的getAnnotation()方法,获取AnnotationField,读取注解对象的descInfo字段。
Class类的getAnnotation方法,返回参数是泛型,继承Annotation接口的类。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
A annotation = getDeclaredAnnotation(annotationClass);
if (annotation != null) {
return annotation;
}
if (annotationClass.isDeclaredAnnotationPresent(Inherited.class)) {
for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
annotation = sup.getDeclaredAnnotation(annotationClass);
if (annotation != null) {
return annotation;
}
}
}
return null;
}
调用native方法,最终获取注解,注解方法和注解变量,同样调用native方法,获取注解。
public native Annotation[] getDeclaredAnnotations();
@注解,实现Annotation接口。
运行时注解动态代理对象运行时反射获取的AnnotationClass注解是动态代理对象$Proxy2,继承class java.lang.reflect.Proxy类,实现注解接口中的方法。
Proxy2对象的createTime()方法时,调用Proxy的invoke方法,内部h的invoke方法,即InvocationHandler。
内部h是AnnotationFactory,注解的变量值在AnnotationMember中保存,h.invoke方法,将对应值返回。
AnnotationFactory类实现InvocationHandler接口在Android系统源码中找到AnnotationFactory,它实现InvocationHandler接口。
AnnotationFactory的invoke方法注解本质是继承Annotation的接口,getAnnotation()方法获取的具体实现类是继承Proxy的动态代理对象$Proxy2,实现注解接口。
三、自定义注解处理器
编译时注解,需要自己写注解处理器
1,创建一个Java Library类型的library,注解和注解处理器的library可以分开。
//使用注解
implementation project(':annotions-lib')
//注解的处理,替代以前的android-apt
annotationProcessor project(':complier-lib')
annotationProcessor是一中apt工具,内置的。在编译的时候会执行依赖的complier-lib库,生成.java文件,库本身不会将它打包到apk。
2,注意,要在library中创建一个注解处理器类,继承AbstractProcessor,而不是在主App中创建。
//注解处理器,编译时,编译器会自动查找所有继承AbstractProcessor的类,触发process方法
@SupportedAnnotationTypes("xxx.xxx.xxx.AppAnnotClass")//注解类路径
@AutoService(Processor.class)
public class AnnotationProcessor extends AbstractProcessor {
写完了注解处理器,还需要将它注册都javac中,用@AutoService注解。
AutoService是google的,用于自动生成META-INF。在build目录下生成。
该文件下保存你写的自定义xxx.xxx.xxx.AnnotationProcessor,注解处理器的名称。
javac会自动检查读取该文件下的注解处理器,注册到javac中,javac会选择它为注解处理器,才能在编译时执行里面的代码。
APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,找出其中的注解,然后可以根据注解生成额外的.java文件作为输出。简单来说就是在编译期,通过注解生成.java文件。
APT的优点就是方便、简单,可以少些很多重复的代码,很多框架Dagger、ButterKnife都是注解框架,可以少些很多代码,只要写一些注解就可以了。
四、调试方法
1,在gradle.properties文件加入代码
org.gradle.jvmargs= -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
org.gradle.parallel=true
2,在Edit Configurations中创建一个Remote。
Edit Configurations Debug Configurations3,创建Apply后,在自定义AnnotationProcessor中打上断点,启动Debug调试。
Debug4,最后,Rebuild Project,就会运行到断点位置调试。
自定义注解处理器调试任重而道远
网友评论