IOC架构设计之ButterKnife源码&原理(二)上篇
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
一、代码结构
我们这里对ButterKnife的最新版本8.4.0进行分析。
我们先down下来看下代码的结构,可以看到代码结构分的还是很好的。
- butterknife ;android library model 提供android使用的API
- butterknife-annotations; java-model,使用时的注解
- butterknife-compiler;java-model,编译时用到的注解的处理器
- butterknife-gradle-plugin;自定义的gradle插件,辅助生成有关代码
- butterknife-integration-test;该项目的测试用例
- butterknife-lint;该项目的lint检查
- sample;demo
这里重点分析butterknife-compiler及butterknife
二、原理图
建议看完全篇了再返回看这个图,会更明白。
三、使用方法
这里不再给出。不过会在原代码分析的时候给出一些注意的地方。
我们拿官方的demo-SimpleActivity
编译完后最后生成的文件为:SimpleActivity_ViewBinding
路径在:
内容:
我们看到了我们熟悉的代码,虽然比较乱(因为是生成的),
可以看出 在构造中findview 在unbind中进行置null处理,让告诉gc在合适的机会回收占用的内存 ;但是这是后面真正生成代码我们看不到的,没关系 嘻嘻。
3.1 整体的原理-(编译时期-注解处理器)
在java代码的编译时期,javac 会调用java注解处理器来进行处理。因此我们可以定义自己的注解处理器来干一些事情。一个特定注解的处理器以 java 源代码(或者已编译的字节码)作为输入,然后生成一些文件(通常是.java文件)作为输出。因此我们可以在用户已有的代码上添加一些方法,来帮我们做一些有用的事情。这些生成的 java 文件跟其他手动编写的 java 源代码一样,将会被 javac 编译。(个人参考及个人理解)
3.2 定义处理器 继承AbstractProcessor
在java中定义自己的处理器都是继承自AbstractProcessor
前3个方法都试固定写法,主要是process方法。
3.3 注册你的处理器
要像jvm调用你写的处理器,你必须先注册,让他知道。怎么让它知道呢,其实很简单,google 为我们提供了一个库,简单的一个注解就可以。
首先是依赖
3.4 基本概念
Elements:一个用来处理Element的工具类
Types:一个用来处理TypeMirror的工具类
Filer:你可以使用这个类来创建.java文件
四、源码分析
分析之前呢先要有写基本的概念
可以看到AutoService注解
(1)init 方法,这个主要是获取一些辅助类
(2)getSupportedSourceVersion()方法就是默认的,获取你该处理器使用的java版本
(3)接下来就是process方法,这个方法是核心方法。
显然分两步执行,下面再仔细走一下该方法流程。
每个注解的查找与解析-findAndParseTargets
第一步
我们先看一下参数 RoundEnvironment 这个是什么呢?个人理解是注解框架里的一个工具什么工具呢?一个可以在处理器处理该处理器 用来查询注解信息的工具,当然包含你在getSupportedAnnotationTypes注册的注解。
第二步
创建了一个LinkedHashMap保证了先后的顺序就是先注解的先生成java文件,其实也没有什么先后无所谓。最后将其返回。
我们这里只分析一个具有代表性的BindView注解,其它的都是一样的,连代码都一毛一样。
在这之前先看一下Element这个类我们看一下官方注释
在注解处理器中,我们扫描 java 源文件,源代码中的每一部分都是Element的一个特定类型。换句话说:Element代表程序中的元素,比如说 包,类,方法。每一个元素代表一个静态的,语言级别的结构.
比如:
可以看到类为TypeElement,变量为VariableElement,方法为ExecuteableElement
这些都是Element的子类,核对源码。
Elements代表源代码,TypeElement代表源代码中的元素类型,例如类。然后,TypeElement并不包含类的相关信息。你可以从TypeElement获取类的名称,但你不能获取类的信息,比如说父类。这些信息可以通过TypeMirror获取。你可以通过调用element.asType()来获取一个Element的TypeMirror。
这个是对于理解源码的基础。
继续,我们看到了for循环查找所有包含BindView的注解。
把element和targetClassMap传入
element.getEnclosingElement();是什么呢?是父节点。就是上面我们说的。
基本的步骤就是上面的5步
1.检查用户使用的合法性
上面的代码里遇见注释了,这里说一下也就是我们在使用bindview注解的时候不能用使用
接下来还有一个方法isBindingInWrongPackage。
这个看名字也才出来个大概 就是不能在android ,java这种源码的sdk中使用。如果你的包名是以android或者java开头就会抛出异常。
到这里合法性检查就完了,如果你使用不当,就会抛出异常。
这里说明一点,在处理器中抛出异常,你不能直接像平常写java代码一样new thow xxx 一样,这样抛出去的异常不太好看。所以java处理器帮我们提供了一个辅助类Messager,这个可以帮助我们
比如检查使用合法性抛出的异常信息-error方法最后都会调用
至此完成了检查。
原文链接https://blog.csdn.net/ta893115871/article/details/52497297