基於版本:
implementation 'com.jakewharton:butterknife:10.2.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
首先經過編譯,註解處理器會生成一個類文件,這個類文件命名方式是:綁定類型名+“_ViewBinding”,如:ButterKnife_ViewBinding在這個編譯類中,繼承自Unbinder接口類,有兩個構造方法,並實現了unbind方法。。
在構造方法中,會對當前綁定類中所有被註解修飾的成員變量或方法,都生成對應的解析代碼
如:針對綁定類中的
@BindView(R.id.textView)
TextView bk;
對應的在新生成的編譯類中的構造方法會實現以下代碼:
target.bk = Utils.findRequiredViewAsType(source, R.id.textView, "field 'bk'", TextView.class);
這樣bk這個控件,就可以拿來使用。其具體的初始化在findRequiredViewAsType中完成
我們可以來看findRequiredViewAsType這個方法,如下:
public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
Class<T> cls) {
View view = findRequiredView(source, id, who);
return castView(view, id, who, cls);
}
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.");
}
到了這裏,發現,最終還是通過findViewById來查找到控件。。
也就是說,ButterKnife這個註解框架的解析是在這個編譯類中實現的
那這個編譯新生成的ButterKnife_ViewBinding類,它啥時候被調用呢????
我們可以看bind方法,如下:
#code_1
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
#code_2
public static Unbinder bind(@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);
}
}
#code_3
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
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);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
從上述代碼,我們可以知道,首先會去LinkedHashMap中取出一個構造器方法對象,這個集合中存儲的構造器方法對象其實就是前面編譯類生成的ButterKnife_ViewBinding的構造器方法,若存在的話,直接拿來用,然後調用newInstance方法,從而創建構造器對象,這樣的話就實現了編譯類中構造方法的調用。
如果集合中不存在,則會先通過類加載器去把編譯類ButterKnife_ViewBinding進行加載,然後通過調用類的getConstructor方法,來獲取到類的構造方法,最後把這個構造器方法存入LinkedHashMap集合中。然後在調用newInstance方法,實現構造方法對象的創建。。。
ps: 註解處理器:在編譯代碼的同時,會處理使用過註解的類,會對應的生成一個新的Java類,一般來說,
這個Java類在程序員運行時會被調用到,因爲這個新生成的類一般都是用來對註解進行解析的。。