ButterKnife框架原理分析

基於版本:

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類在程序員運行時會被調用到,因爲這個新生成的類一般都是用來對註解進行解析的。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章