butterknife是一个Android View和Callback注入框架,相信很多人都在使用,可以减少很多代码量,并且避免遗漏绑定而产生的异常。
1. 介绍
1.1 优点
- 通过注释@BindView来消除findViewById
- 通过注释@OnClick或者其他来消除绑定事件
- 通过资源注释消除资源查找。
一句话概括,用自动生成的代码来帮助处理以上事情,减少开发者的代码量。
2. 基本用例
因为比较简单,可以直接参考butterknife官方介绍。下面就以最常用的View绑定和点击事件绑定为例。
2.1 使用@BindView绑定View
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tvResult) TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tvResult.setText("Test");
}
}
2.2 使用@OnClick绑定点击事件
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.btnFinish)
void btnFinishClick() {
finish();
}
}
3. 源码分析
从上图我们可以看到,butterknife的源码主要由2部分组成,一部分是编译期自动生成代码,另一部分是通过自动生成的代码对View或者点击事件进行绑定。
3.2 自动代码生成
我们可以看到,每一个Activity(或者Fragment等),生成2个类,分别是"ClassName_ViewBinder"和“ClassName_ViewBinding”。
生成代码
主要用了代码生成框架auto,以后会专门结合ButterKnife写一篇文章,本文点到为止,你只要知道解析注解,通过Auto框架生成了以上的Java文件,不做具体展开。
主要逻辑在ButterKnifeProcessor里的process接口
@Override public boolean process(Set extends TypeElement> elements, RoundEnvironment env) {
//寻找并且生成BindingClass
Map targetClassMap = findAndParseTargets(env);
for (Map.Entry entry : targetClassMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingClass bindingClass = entry.getValue();
//依次生成Java文件
for (JavaFile javaFile : bindingClass.brewJava()) {
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write view binder for type %s: %s", typeElement,
e.getMessage());
}
}
}
return true;
}
3.3 View绑定
通过ButterKnife进行关联
ButterKnife.bind(this);
public static Unbinder bind(@NonNull Activity target) {
return getViewBinder(target).bind(Finder.ACTIVITY, target, target);
}
根据Activity获取ViewBinder
static ViewBinder
根据class名称获取ViewBinder
private static ViewBinder
调用MainActivity_ViewBinder的bind
public final class MainActivity_ViewBinder implements ViewBinder {
@Override
public Unbinder bind(Finder finder, MainActivity target, Object source) {
return new MainActivity_ViewBinding<>(target, finder, source);
}
}
实例化ClassName_ViewBinding类
public class MainActivity_ViewBinding implements Unbinder {
protected T target;
private View view2131492946;
public MainActivity_ViewBinding(final T target, Finder finder, Object source) {
this.target = target;
View view;
//查找View,并赋值给Activity的tvResult
target.tvResult = finder.findRequiredViewAsType(source, R.id.tvResult, "field 'tvResult'", TextView.class);
...省略代码
}
}
查找View
public final T findRequiredViewAsType(Object source, @IdRes int id, String who,
Class cls) {
//查找View
View view = findRequiredView(source, id, who);
return castView(view, id, who, cls);
}
public final View findRequiredView(Object source, @IdRes int id, String who) {
//查找类
View view = findOptionalView(source, id);
if (view != null) {
return view;
}
...省略代码
}
ACTIVITY {
@Override public View findOptionalView(Object source, @IdRes int id) {
//通过Activity查找View
return ((Activity) source).findViewById(id);
}
...省略代码
}
至此完成了View的绑定流程,点击事件和资源文件绑定流程大同小异,我就不一一介绍了,感兴趣的可以自己看下。
6. 参考资料
butterknife
butterknife官方介绍
auto
可以随意转发,也欢迎关注我的,我会坚持给大家带来分享。