美文网首页安卓开源框架源码解析
butterknife源码解析,看完不懂你打我

butterknife源码解析,看完不懂你打我

作者: f36b3e910208 | 来源:发表于2018-06-15 00:48 被阅读15次

    首先说下butterknife原理

    编译时扫描注解并通过javapoet库生成Java代码,调用ButterKnife.bind()方法将ID与对应的上下文绑定在一起(直接就说结论后面还会有人看吗少侠留步,别那么浮躁静下心来慢慢看)

    butterknife是一个注解库也就是通过注解来实现View注入的,同为注解库为啥butterknife就这多Star呢,早期的注解库的生命周期是RUNTIME,在运行时通过反射来完成注入,Activity运行时大量使用反射会影响App性能同时产生很多临时对象造成gc,导致界面卡顿。而butterknife采用的是编译时解析技术,在编译时生成代码,所以对运行时没有任何影响。

    在分析源码前先简单补习两点知识

    元注解

    元注解就是注解注解的注解,嗯!

    主要有几种其中我们本次讲解用到的有@Target、@Retention。

    @Target:标明了注解的使用范围,常用的有

    @Retention:用来描述注解的生命周期,有三种

    1)SOURCE

    被编译器忽略

    2)CLASS

    注解将会被保留在Class文件中,但在运行时并不会被JVM保留。这是默认行为,所有没有用Retention注解的注解,都会采用这种策略。

    3)RUNTIME

    保留至运行时。所以我们可以通过反射去获取注解信息。

    看下butterknife源码

    注解处理器

    注解处理器(AbstractProcessor)是javac的一个工具,利用这个工具可以在编译时扫描和处理注解,包括自定义注解,只要定义相应的注解处理器,每个注解处理器都继承AbstractProcessor类,AbstractProcessor类中有几个重要的方法

    1)init(ProcessingEnvironment var1)

    ProcessingEnvironment是一个接口,里面有三个重要的方法,如图

    getElementUtils()方法返回一个Elements,Elements是用于处理Element的工具类,而Element由是什么呢?在注解处理过程中,扫描了所有的java源文件,我的理解是源文件抽象成类就是Element(瑟瑟发抖...不对的话请大佬指出)

    Types是用于处理TypeElement的工具类,TypeElement代表源代码中的类型

    Filer从文字就可以看出用于创建文件的

    2)public abstract boolean process(Set var1, RoundEnvironment var2)

    这个方法用于扫描和处理注解,最后会生成我们所需要的java代码,通过RoundEnvironment参数可以查到包含特定注解的原属,需要自定义处理器实现。

    3)public Set getSupportedAnnotationTypes()

    返回注解所支持的类型?no no no 返回所支持的注解的类型,注意这是不一样的。

    4)public SourceVersion getSupportedSourceVersion()

    指定所使用的java版本

    需要注意的是注解处理器是运行在自己的java虚拟机中的

    下面正式开始分析butterknife原理

    可以看见这就是butterknife支持的注解

    在process方法中会拿到所有的注解信息放到一个Map中,然后遍历Map做相应处理然后生成Java代码

    findAndParseTargets(RoundEnvironment env)方法就是用于处理每一个注解的,方法很长,我们只看BindView就可以,其他逻辑一样的

    看到主要逻辑都在parseBindView(Elementelement,MapbuilderMap,

    SeterasedTargetNames)方法

    这个方法会判断被注解的属性是不是privete或static的,如果是就会出错、包名是不是以android或java开头的,是就出错;判断注解的属性是不是一个View,不是则出错

    然后int id = element.getAnnotation(BindView.class).value()获取到了要绑定的View的id,根据所在元素查找Builder,对Builder进行判断,如果已经存在了则返回else通过getOrCreateBindingBuilder(builderMap, enclosingElement)方法创建Builder,最后放到集合中

    至此已经处理好注解了,下面看下如何生成java代码

    在process方法中调用了brewJava方法

    可以看到内部调用了createType(int sdk, boolean debuggable)来返回我们需要的类型

    里面这个TypeSpec.Builder就是javapoet库构造类的属性用的类,然后就是根据各种判断来创建这个类。119行调用了createBindingConstructor(sdk, debuggable)方法,这个方法就是用于把注解转换成具体代码的,例如把@BindView换成findViewById(),有点长就不贴代码了,方法里面主要做了两件事:判断View是否有监听,如果有就声明称final的;遍历绑定的View,调用addViewBinding生成对应的findViewById方法

    洋洋洒洒的把butterknife原理说完了,表达能力有限,反正也没人看就写到这吧

    相关文章

      网友评论

        本文标题:butterknife源码解析,看完不懂你打我

        本文链接:https://www.haomeiwen.com/subject/souyeftx.html