1、 ButterKnife.bind()
綁定到 Activity
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
// 通過target 獲取到當前xml 布局
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
綁定到 View
/**
* BindView annotated fields and methods in the specified {@link View}. The view and its children
* are used as the view root.
*
* @param target Target view for view binding.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull View target) {
return bind(target, target);
}
綁定到 Dialog
/**
* BindView annotated fields and methods in the specified {@link Dialog}. The current content
* view is used as the view root.
*
* @param target Target dialog for view binding.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Dialog target) {
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
綁定到Fragment 上
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link View} as the view root.
*
* @param target Target class for view binding.
* @param source View root on which IDs will be looked up.
*/
@NonNull @UiThread
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);
}
}
bind 方法
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link View} as the view root.
*
* @param target Target class for view binding.
* @param source View root on which IDs will be looked up.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
// 通過 target 獲取到類的實例
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
// 通過class 查找 construtor 即編譯生成的 classname_VViewBinding 類
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
// 構(gòu)造出一個 Unbinder
return constructor.newInstance(target, source);
}
// code
}
看下 findBindingConstructorForClass 方法
2、根據(jù)class獲取目標Class的包名+類名,根據(jù)拿到的包名+類名+_ViewBinding構(gòu)造一個構(gòu)造函數(shù),然后傳入targe和target.getWindow().getDecorView(),實例化構(gòu)造函數(shù)。此時bind方法的任務就完成了
//緩存集合
@VisibleForTesting
static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();
@Nullable @CheckResult @UiThread
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;
}
// 獲取當前類的類名,如果是系統(tǒng)名稱,返回null
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;
}
// 如果緩存中沒有,就直接創(chuàng)建一個
try {
//獲取cls的classloader并加載cls.getName()+_ViewBinding從而創(chuàng)建一個新類的Constructor的Class。而加載的這個新的類是項目在編譯期間用注解編輯器生成的。
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
//從bindingClass類中獲取到Constructor對象
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
}
// code ellipsis ...
//把新類放入緩存中下次使用,key=subscriber.getClass(),value=包名+subscriber類名_ViewBinding
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
看看 MainActivity_ViewBinding 這個類
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
private View view7f080433;
@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;
target.noScrollViewPager = Utils.findRequiredViewAsType(source, R.id.noScrollViewPager, "field 'noScrollViewPager'", NoScrollViewPager.class);
target.commonTabLayout = Utils.findRequiredViewAsType(source, R.id.commonTabLayout, "field 'commonTabLayout'", CommonTabLayout.class);
view = Utils.findRequiredView(source, R.id.pop_img, "field 'popImg' and method 'onClick'");
target.popImg = Utils.castView(view, R.id.pop_img, "field 'popImg'", ImageView.class);
view7f080433 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.onClick(p0);
}
});
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.noScrollViewPager = null;
target.commonTabLayout = null;
target.popImg = null;
view7f080433.setOnClickListener(null);
view7f080433 = null;
}
}
- 本質(zhì)上還是通過findViewById 查找view的,只是這部分代碼不需要我們自己寫了而已,框架在編譯時會自動幫我們生成好了。
3、其實ButterKnife 核心是 AnnotationProcessor(注解編譯器) 和 JavaPoet,來生成ViewBinding 類....