一、先看看ButterKnife的簡單使用
1. 導入ButterKnife
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
2. MainActivity(綁定當前view)
@BindView(R.id.btn)
Button mButton;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mButton.setText("test");
}
@OnClick(R.id.btn)
public void btnOnclick() {
Intent intent = new Intent(MainActivity.this, Main3Activity.class
);
startActivity(intent);
}
二藕施、源碼分析(ButterKnife.java)
1. 看看bind做了什么事件吞获,其實就是返回一個Unbinder對象谦秧。這個對象是一個(clsName + "_ViewBinding")的類污茵,實現(xiàn)Unbinder。(這里使用的反射)
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull Dialog source) {
View sourceView = source.getWindow().getDecorView();
return createBinding(target, sourceView);
}
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//反射創(chuàng)建Unbinder
return constructor.newInstance(target, source);
}
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
// 緩存的LinkedHashMap 查找當前的activity是否緩存
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
// ViewBinder的子類的類名(MainActivity$$ViewBinder)阵面,然后通過反射的形式創(chuàng)建(MainActivity$$ViewBinder)的實例,并存入緩存
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
2. 看看Unbinder是什么
public interface Unbinder {
@UiThread void unbind();
Unbinder EMPTY = new Unbinder() {
@Override public void unbind() { }
};
3. 看看生成的MainActivity_ViewBinding.java是什么樣子的(在build/generated/source/apt文件夾)
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
private View view2131165218;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(final MainActivity target, View source) {
this.target = target;
View view;
// 主要就是 View view = source.findViewById(id);
view = Utils.findRequiredView(source, R.id.btn, "field 'mButton' and method 'btnOnclick'");
target.mButton = Utils.castView(view, R.id.btn, "field 'mButton'", Button.class);
view2131165218 = view;
// 設置點擊事件
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.btnOnclick();
}
});
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.mButton = null;
view2131165218.setOnClickListener(null);
view2131165218 = null;
}
}
4. 這個類是怎么生成的,使用了APT(編譯時解析技術),對于注解在編譯時候解析是通過我們自定義一個繼承AbstractProcessor的類來完成的样刷。所以我們找找ButterKnife的源碼仑扑,在butterknife-compile (ButterKnifeProcessor.java)
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable);
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
5. 可以看出這個類主要就是做了兩件事,首先是通過findAndParseTargets方法找出該類里所有注解置鼻,然后通過binding.brewJava方法將這些注解進行處理镇饮。
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
// Process each @BindView element.
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
// we don't SuperficialValidation.validateElement(element)
// so that an unresolved View type can be generated by later processing rounds
try {
// 創(chuàng)建一個BindingSet抽象類,然后將我們注解信息處理后保存在里面
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
// .........
return bindingMap;
}
6. 通過binding.brewJava方法將這些注解進行處理箕母。(BindingSet.java)通過JavaFile創(chuàng)建出新的類储藐。而這個新生成的className+_ViewBinding“”類,我們可以在ButterKnife.bind時候獲取到它嘶是,然后就通過這個類里的方法钙勃,實現(xiàn)我們控件和監(jiān)聽方法的綁定了。
JavaFile brewJava(int sdk, boolean debuggable) {
TypeSpec bindingConfiguration = createType(sdk, debuggable);
//采用了JavaPoet聂喇,一個非常強大的代碼生成工具辖源。根據(jù)我們的注解內(nèi)容,通過TypeSpec類和MethodSpec類構(gòu)造出對應的方法希太,然后根據(jù)之前創(chuàng)建BindingSet抽象類時候創(chuàng)建的新類名克饶。
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
三、總結(jié)
使用編譯時注解跛十,在編譯時期自動生成Unbinder(APT)彤路,這個對象自動生成了我們需要的動畫、監(jiān)聽事件等等芥映。
在我們的Activity中使用Bind進行綁定洲尊,我們就執(zhí)行了自動生成的代碼。而且返回一個Unbinder奈偏。
ButterKnife不能將控件和方法設置為private或者static坞嘀,是因為在className_ViewBinder類會直接調(diào)用該控件和方法進行賦值。
編譯時注解+APT
編譯時注解:@Retention(CLASS)惊来。
APT(Annotation Processing Tool)編譯時解析技術丽涩。