butterknife是一個(gè)Android View和Callback注入框架伤为,相信很多人都在使用啊送,可以減少很多代碼量隅茎,并且避免遺漏綁定而產(chǎn)生的異常鞠眉。
1. 介紹
1.1 優(yōu)點(diǎn)
- 通過注釋@BindView來消除findViewById
- 通過注釋@OnClick或者其他來消除綁定事件
- 通過資源注釋消除資源查找邑商。
一句話概括,用自動(dòng)生成的代碼來幫助處理以上事情凡蚜,減少開發(fā)者的代碼量人断。
2. 基本用例
因?yàn)楸容^簡(jiǎn)單,可以直接參考butterknife官方介紹朝蜘。下面就以最常用的View綁定和點(diǎn)擊事件綁定為例恶迈。
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綁定點(diǎn)擊事件
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部分組成谱醇,一部分是編譯期自動(dòng)生成代碼暇仲,另一部分是通過自動(dòng)生成的代碼對(duì)View或者點(diǎn)擊事件進(jìn)行綁定。
3.2 自動(dòng)代碼生成
我們可以看到副渴,每一個(gè)Activity(或者Fragment等)奈附,生成2個(gè)類,分別是"ClassName_ViewBinder"和“ClassName_ViewBinding”煮剧。
生成代碼
主要用了代碼生成框架auto斥滤,以后會(huì)專門結(jié)合ButterKnife寫一篇文章,本文點(diǎn)到為止勉盅,你只要知道解析注解佑颇,通過Auto框架生成了以上的Java文件,不做具體展開草娜。
主要邏輯在ButterKnifeProcessor里的process接口
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//尋找并且生成BindingClass
Map<TypeElement, BindingClass> targetClassMap = findAndParseTargets(env);
for (Map.Entry<TypeElement, BindingClass> 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進(jìn)行關(guān)聯(lián)
ButterKnife.bind(this);
public static Unbinder bind(@NonNull Activity target) {
return getViewBinder(target).bind(Finder.ACTIVITY, target, target);
}
根據(jù)Activity獲取ViewBinder
static ViewBinder<Object> getViewBinder(@NonNull Object target) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());
return findViewBinderForClass(targetClass);
}
根據(jù)class名稱獲取ViewBinder
private static ViewBinder<Object> findViewBinderForClass(Class<?> cls) {
//根據(jù)cls獲取緩存的ViewBinder
ViewBinder<Object> viewBinder = BINDERS.get(cls);
//如果緩存存在挑胸,則直接返回使用
if (viewBinder != null) {
if (debug) Log.d(TAG, "HIT: Cached in view binder map.");
return viewBinder;
}
String clsName = cls.getName();
//如果類名是以android或者java開頭,則認(rèn)為是框架類宰闰,不作處理
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return NOP_VIEW_BINDER;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
//根據(jù)clsName + "_ViewBinder" 實(shí)例化
Class<?> viewBindingClass = Class.forName(clsName + "_ViewBinder");
//noinspection unchecked
viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();
if (debug) Log.d(TAG, "HIT: Loaded view binder class.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
//如果沒有找到該類茬贵,則實(shí)例化父類
viewBinder = findViewBinderForClass(cls.getSuperclass());
} catch (InstantiationException e) {
throw new RuntimeException("Unable to create view binder for " + clsName, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to create view binder for " + clsName, e);
}
//將實(shí)例化的viewBinder進(jìn)行緩存
BINDERS.put(cls, viewBinder);
return viewBinder;
}
調(diào)用MainActivity_ViewBinder的bind
public final class MainActivity_ViewBinder implements ViewBinder<MainActivity> {
@Override
public Unbinder bind(Finder finder, MainActivity target, Object source) {
return new MainActivity_ViewBinding<>(target, finder, source);
}
}
實(shí)例化ClassName_ViewBinding類
public class MainActivity_ViewBinding<T extends MainActivity> 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> T findRequiredViewAsType(Object source, @IdRes int id, String who,
Class<T> 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的綁定流程移袍,點(diǎn)擊事件和資源文件綁定流程大同小異解藻,我就不一一介紹了,感興趣的可以自己看下咐容。
6. 參考資料
butterknife
butterknife官方介紹
auto
可以隨意轉(zhuǎn)發(fā)舆逃,也歡迎關(guān)注我的簡(jiǎn)書蚂维,我會(huì)堅(jiān)持給大家?guī)矸窒怼?/p>