ButterKnife原理分析
ButterKnife的分析文章很多了,這裡只是簡單分析原理,不想看程式碼,可以直接拉到後面總結。
ButterKnife最簡單的使用方法如下:
public class SimpleActivity extends Activity {
@BindView(R.id.title)
TextView title;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
title.setText("Butter Knife");
}
}
從入口ButterKnife.bind(this)開始分析:
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();//獲得Activity的DecorView
return createBinding(target, sourceView);
}
首先獲得Activity最頂層的DecorView,有了DecorView,後面就可以通過獲得佈局的所有View。往下走到了createBinding,引數是Activity和DecorView:
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;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
createBinding首先要通過findBindingConstructorForClass去獲得該Activity對應的binding類,binding類在build/generated/source/apt/debug或release的包下面,比如,上面的SimpleActivity,通過findBindingConstructorForClass方法找到的類是以上目錄下的SimpleActivity_ViewBinding。這裡先不用管findBindingConstructorForClass是怎麼找到SimpleActivity_ViewBinding和SimpleActivity_ViewBinding是怎麼生成的,看看找到後,做了什麼事:return constructor.newInstance(target, source);找到後呼叫了構造方法。上面一大段無非是呼叫了SimpleActivity_ViewBinding的構造方法,來看看SimpleActivity_ViewBinding的程式碼:
public class SimpleActivity_ViewBinding implements Unbinder {
private SimpleActivity target;
private View view2130968578;
@UiThread
public SimpleActivity_ViewBinding(final SimpleActivity target, View source)
{
this.target = target;
target.title = Utils.findRequiredViewAsType(source, R.id.title, "field 'title'", TextView.class);
}
public void unbind() {
SimpleActivity target = this.target;
this.target = null;
target.title = null;
}
}
上面的target就是最初的SimpleActivity,看到target.title就知道為什麼在SimpleActivity裡TextView title為什麼不能宣告為private變數。target.title是通過
Utils.findRequiredViewAsType獲得的,看引數,findViewById需要的東西都有了,可見findRequiredViewAsType肯定是findViewById的封裝方法,進去看看:
public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
Class<T> cls) {
View view = findRequiredView(source, id, who);//獲得R.id.title的View
return castView(view, id, who, cls);//將上面的View轉化成TextView型別
}
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);//這麼多行,就這句最重要
if (view != null) {
return view;
}
String name = getResourceEntryName(source, id);
throw new IllegalStateException("Required view '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
+ " (methods) annotation.");
}
public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {
try {
return cls.cast(view);
} catch (ClassCastException e) {
String name = getResourceEntryName(view, id);
throw new IllegalStateException("View '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was of the wrong type. See cause for more info.", e);
}
}
上面程式碼一大把,其實就做了兩件事,獲得R.id.title的View,將View轉化成真正的型別TextView,完成了id到View的繫結。
以上就是ButterKnife的原理,再總結下工作流程:
1、用@BindView註解View的ID和View變數。
2、apt會根據註解生成該Activity對應的Activity_ViewBinding類。
3、在Activity中呼叫 ButterKnife.bind(this)。
4、ButterKnife通過bind引數可以獲得當前的Activity類名和最頂層的DecorView。
5、通過類名獲得該類對應的Activity_ViewBinding類,並呼叫Activity_ViewBinding的構造方法初始化。
6、在Activity_ViewBinding的構造方法中通過封裝的findViewById獲得該id對應的View,並通過cast方法轉化成真正的View型別,比如TextView。
額外的話題
上面有兩個問題我們先沒有管:
1、SimpleActivity_ViewBinding是怎麼生成的?
2、findBindingConstructorForClass是怎麼找到SimpleActivity_ViewBinding?
第一個問題其實是java註解的應用,比如@BindView,apt通過這些註解生成一個新的java檔案,當然生成邏輯得自己寫,這裡比較複雜,可以參考原始碼。
第二個問題看原始碼:
static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap();
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = (Constructor)BINDINGS.get(cls);
if(bindingCtor != null) {
if(debug) {
Log.d("ButterKnife", "HIT: Cached in binding map.");
}
return bindingCtor;
} else {
String clsName = cls.getName();
if(!clsName.startsWith("android.") && !clsName.startsWith("java.")) {
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
bindingCtor = bindingClass.getConstructor(new Class[]{cls, View.class});
if(debug) {
Log.d("ButterKnife", "HIT: Loaded binding class and constructor.");
}
} catch (ClassNotFoundException var4) {
if(debug) {
Log.d("ButterKnife", "Not found. Trying superclass " + cls.getSuperclass().getName());
}
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException var5) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, var5);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
} else {
if(debug) {
Log.d("ButterKnife", "MISS: Reached framework class. Abandoning search.");
}
return null;
}
}
}
首先在BINDINGS這個快取map裡去找對應的Activity_ViewBinding,找到了直接返回。沒找到話往下走。這時候因為有了Activity類名,要找到Activity_ViewBinding只需要找Activity類名+"_ViewBinding"字尾的類就行了,當然系統類不用找了,所以有個"android."和"java."字首的判斷,看下怎麼找的:
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
找到了Activity_ViewBinding類bindingClass,再獲得構造方法:
bindingCtor = bindingClass.getConstructor(new Class[]{cls, View.class});
最後把找到的Activity_ViewBinding快取起來:
BINDINGS.put(cls, bindingCtor);
返回Activity_ViewBinding的構造方法結束。
相關文章
- Butterknife原理分析及自己實現Butternife
- butterknife原始碼簡單分析&原理簡述原始碼
- 從 java 註解分析 ButterKnife 工作流程Java
- ButterKnife 從入門到精通 - 原始碼級分析(二)原始碼
- Android ButterknifeAndroid
- 【天星技術團隊】從自定義Annotation、APT、JavaPoet再到Butterknife原理APTJava
- ButterKnife原始碼解析原始碼
- Butterknife 8.8.1原始碼解析:原始碼
- 選擇ButterKnife,告別findViewByIdView
- androidstudi3.0 butterknife的使用Android
- android注入之ButterKnife的使用Android
- Xposed原理分析
- Handler原理分析
- AST 原理分析AST
- Promise原理分析Promise
- SparseIntArray原理分析
- SparseArray原理分析
- HSF原理分析
- KVO原理分析
- mysqldump原理分析MySql
- butterknife在eclipse配置講解(解決ButterKnife.bind初始化無效問題)Eclipse
- ThreadLocal原理分析thread
- ReentrantLock原理分析ReentrantLock
- isMemberOfClass、isKindOfClass原理分析
- queue:work 原理分析
- SpringIOC原理分析Spring
- redis client原理分析Redisclient
- Docker 工作原理分析Docker
- JavaScript ==原理與分析JavaScript
- ThreadLocal 原理分析thread
- OpenAll:Android開啟元件新姿勢【僅供用於學習瞭解ButterKnife框架基本原理】Android元件框架
- 資料中介者:DataMediator, (ButterKnife最佳拍檔)
- Spark原始碼分析之cahce原理分析Spark原始碼
- mr原理簡單分析
- CPU效能分析工具原理
- 淺談IntentService原理分析Intent
- jquery中on方法原理分析jQuery
- RabbitMq底層原理分析MQ