butterknife原理:编译时注解。
java注解(四种元注解:@Retention @Target @Document @Inherited)
- @Document:说明该注解将被包含在javadoc中
- @Retention:注解的保留位置
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到 - @Target:注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包 - @Inherited:说明子类可以继承父类中的该注解
APT(注解处理器)
定义:
APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。 简单来说就是在编译期,通过注解生成.java文件。
- 注解处理器(Annotation Processor)是Javac的一个工具。
- 每一个处理器都是继承于AbstractProcessor
- init方法中的入参ProcessingEnvironment 的变量Filer ,Elements ,Types
AbstractProcessor
APT大概就是你声明的注解的生命周期为CLASS,然后继承AbstractProcessor类。继承这个类后,在编译的时候,编译器会扫描所有带有你要处理的注解的类,然后再调用AbstractProcessor的process方法,对注解进行处理,那么我们就可以在处理的时候,动态生成绑定事件或者控件的java代码,然后在运行的时候,直接调用bind方法完成绑定。
AbstractProcessor的四个核心方法:
init;process;getSupportedAnnotationTypes;getSupportedSourceVersion
其中process最核心
AbstractProcessor的init方法如下:
public synchronized void init(ProcessingEnvironment var1) {
ProcessingEnvironment 里面的重要变量
public interface ProcessingEnvironment {
Map<String, String> getOptions();
Messager getMessager();
//创建文件用的
Filer getFiler();
//源代码
Elements getElementUtils();
//源代码类型信息
Types getTypeUtils();
SourceVersion getSourceVersion();
Locale getLocale();
}
AbstractProcessor里面最核心的是process方法
拓展:
getSupportedAnnotationTypes
//返回支持的注解的类型
public Set<String> getSupportedAnnotationTypes() {
SupportedAnnotationTypes var1 = (SupportedAnnotationTypes)this.getClass().getAnnotation(SupportedAnnotationTypes.class);
if (var1 == null) {
if (this.isInitialized()) {
this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedAnnotationTypes annotation found on " + this.getClass().getName() + ", returning an empty set.");
}
return Collections.emptySet();
} else {
return arrayToSet(var1.value());
}
}
//指定你所适用的Java版本
public SourceVersion getSupportedSourceVersion() {
SupportedSourceVersion var1 = (SupportedSourceVersion)this.getClass().getAnnotation(SupportedSourceVersion.class);
SourceVersion var2 = null;
if (var1 == null) {
var2 = SourceVersion.RELEASE_6;
if (this.isInitialized()) {
this.processingEnv.getMessager().printMessage(Kind.WARNING, "No SupportedSourceVersion annotation found on " + this.getClass().getName() + ", returning " + var2 + ".");
}
} else {
var2 = var1.value();
}
return var2;
}
APT的流程:
1、声明的注解的生命周期为CLASS
2、继承AbstractProcessor类(编辑器会扫描要处理的注解)
3、调用process处理注解
拓展下反射
反射+运行时注解举例
运行时获取和使用类信息
反射的作用
1、判断任意一个对象所属的类
2、构造任意一个类的对象
3、判断任意一个类所具有的成员变量和方法(反射甚至可以调用private方法)
4、调用任意一个对象的方法
反射的简单使用:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestInterface {
int value() default 20;
}
//客户端调用
public class ReflecMain {
@TestInterface(88)
public int age;
public void handle() {
try {
Class clazz = ReflecMain.class;
Field field = clazz.getField("age");
TestInterface testInterface = field.getAnnotation(TestInterface.class);
int value = testInterface.value();
//可以得到value的值的88
Log.v("tag", "value==" + value);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
butterknife的工作原理:
1、编译的时候扫描注解,并做相应的处理,生成java代码,生成java代码是调用javapoet库生成的,同时通过@click注解编译的时候动态生成java文件,就是利用apt,编译器又会把java文件编译成class文件。
2、调用ButterKnife.bind(this)方法的时候,会将ID和上下文绑定在一起
总结:
通过process方法处理注解,调用squre公司开源的javapoet库的brewJava方法来生成代码,然后通过brewJava中createType方法生成一些类型和添加findviewbyid。
最终总结:
直接看AbstractProcessor的实现类:ButterKnifeProcessor的process方法,process方法拿到注解后放在一个HashMap中,遍历集合,通过squre公司开源的javapoet库生成java代码。
butterknife的原理:简单来说就是通过APT生成java代码(就是把注解的代码生成正常的findviewbyid的代码就可以了)。