ButterKnife的原理简述java
注解处理器
Java5 中叫APT(Annotation Processing Tool),在Java6开始,规范化为 Pluggable Annotation Processing。android
找到全部被注解的属性或者方法,将全部的信息收集到对应的“类数据集”中。缓存
根据每个“类数据集”,生成对应的java源文件。因为这些文件并非在运行时生成的,所以也无需动态编译,注解处理器运行完成以后,
编译器会处理全部的编译流程。app
运行时动态注入,即用户常规调用的 ButterKnife.bind(activity)ide
这一步为了不蹩脚的调用,使用了运行时反射,可是做者对每个类进行了缓存,所以,不会对执行效率产生多大影响。工具
在最新的 ButterKnife 源码(2015.06.08)中,ButterKnife已经重构了部分方法:命令行
ButterKnife#inject -> ButterKnife#bindcode
@InjectView -> @FindView对象
等等,具体变化能够去看官方文档,**本文档后续代码使用最新版本代码演示**。接口
极简实现演示
示例代码由 ButterKnife 简化而来,部分定义和实现有删改,只能绑定 Activity 中的 View 字段 为了不引入Android平台,可是又须要更直观,因此mock了android的两个类,Activity 和 View 为了不使用 Pluggable Annotation Processing 过程当中的jar包要求,以及波及没必要要的java文件,请使用命令行运行演示,直接运行 ./run.sh 便可查看结果 保证 CLASSPATH 中含有tools.jar
在每个类中找到全部被 FindView 注解的字段
每个须要绑定的字段信息都保存为一个 FieldViewBinding 对象,好比:
@FindView(100) View vView1; 获得: new FieldViewBinding(vView1, android.view.View, 100)
将字段分类,获取每个类的“类数据集”BindingClass,好比, MainActivity 对应的 “类数据集” 以下:
MainActivity: List<FieldViewBinding> fieldViewBindings = new ArrayList<FieldViewBinding>(); fieldViewBindings.add(new FieldViewBinding(vView1, android.view.View, 100)) fieldViewBindings.add(new FieldViewBinding(vView2, android.view.View, 200))
为了便于在反射时容易实例化生成的类,每个生成的类都实现了一个 ActivityBinder 接口,所以,根据 MainActivity “类数据集”生成的文件以下:
package sample; import android.view.View; import android.app.Activity; import butterknife.ButterKnife.ActivityBinder; public class MainActivity$$ViewBinder implements ActivityBinder<sample.MainActivity> { @Override public void bind(sample.MainActivity target) { View view; view = target.findViewById(100); target.vView1 = view; //这里要求 vView1 的访问权限为 package 级别 view = target.findViewById(200); target.vView2 = view; } }
咱们在 MainActivity 中调用 ButterKnife#bind,第一件事就是找到对应生成的 Bind 工具类,这里遵循命名规则(在对应类后增长 $$ViewBinder 后缀),直接使用动态加载并实例化:
Class<?> activityBindingClass = Class.forName(targetClass.getName() + ButterKnifeProcessor.SUFFIX); activityBinder = (ActivityBinder) activityBindingClass.newInstance();
得到相应的 ActivityBinder 以后,使用 ActivityBinder#bind 进行绑定,与手动调用 findViewById 效果相同
运行:
ButterKnifeProcedure/src$ ./run.sh
结果:
mainActivity.vView1.id = 100 mainActivity.vView2.id = 200