butterknife依赖注入框架源码解析

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的代码就可以了)。

上一篇:提取ifc对象


下一篇:2021-02-09