搜索
您的当前位置:首页正文

ButterKnife源码梳理

来源:二三娱乐

最开始版本的butterknife使用反射来实现
<pre>
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
}
</pre>
像这样定义一个注解,然后通过遍历Activity或者Fragment中的成员变量来实现findViewById的过程
<pre>
private static void injectView(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
//获得activity的所有成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//获得每个成员变量上面的ViewInject注解,没有的话,就会返回null
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
int viewId = viewInject.value();
View view = activity.findViewById(viewId);
try {
field.setAccessible(true);
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
</pre>

但是这种方式的性能很低,特别是对类似Android这样的系统,对性能要求很苛刻的情况,最好不要用反射。所以Jake大神进行了修改。
新版本的Butterknife用的APT(Annotation Processing Tool)编译时解析技术。
APT大概就是你声明的注解的生命周期为CLASS,然后继承AbstractProcessor类。继承这个类后,在编译的时候,编译器会扫描所有带有你要处理的注解的类,然后再调用AbstractProcessor的process方法,对注解进行处理,那么我们就可以在处理的时候,动态生成绑定事件或者控件的java代码,然后在运行的时候,直接调用bind方法完成绑定。
先说下butterknife的几个模块:
':butterknife' 静态绑定方法入口,查找生成类并存入缓存
':butterknife-annotations' 定义所有的注解类
':butterknife-compiler' 注解解析
':butterknife-gradle-plugin' 插件
':butterknife-lint'
':butterknife-integration-test' 测试模块

生成类之后如何使用呢,butterknife调用了静态方法bind,在bind方法中调用了createBinding,这个方法是根据 target 创建对应的 targetClassName_ViewBinding对象 。在 targetClassName_ViewBinding 的构造器中会把对应的 View 进行绑定(具体可以查看上面的 MainActivity_ViewBinding )。而在 findBindingConstructorForClass(Class<?> cls) 方法中也使用了 Class.forName() 反射来查找 Class ,这也是无法避免的。但是仅限于一个类的第一次查找,之后都会从 BINDINGS 缓存中获取。

Top