ButterKnife源码分析

本文深入探讨ButterKnife的工作原理,解析其绑定机制,包括如何通过反射获取目标Activity的Class对象,查找并实例化对应的_ViewBinding类,以及绑定视图组件的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析方法,从入口Debug单步调试

入口,从Activity的OnCreate中的 ButterKnife.bind(this) 入手


@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);

拿到DecorView,即根View,传入createBinding方法

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);
}
}

通过反射拿到 target的Class对象,这里的target是要绑定的Activity
然后调用 findBindingConstructorForClass(targetClass);


@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
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;
}

此方法是找出Acitvity_ViewBinding.class的Constructor对象
Activity_ViewBing是ButterKnife编译生成的class
在此class中,将注入的View和Activity关联起来

BINDINGS 是XXXActivity_ViewBinding的缓存,就是LinkedHashMap

在createBindging方法中调用
constructor.newInstance(target, source);
即调用XXXActivity_ViewBindng的构造方法


public class AppSettingsActivity_ViewBinding implements Unbinder {
private AppSettingsActivity target;

private View view2131296303;

private View view2131296299;

private View view2131296479;

@UiThread
public AppSettingsActivity_ViewBinding(AppSettingsActivity target) {
this(target, target.getWindow().getDecorView());
}

@UiThread
public AppSettingsActivity_ViewBinding(final AppSettingsActivity target, View source) {
this.target = target;

View view;
target.mAppName = Utils.findRequiredViewAsType(source, R.id.app_name, "field 'mAppName'", TextView.class);
view = Utils.findRequiredView(source, R.id.app_upgrade, "field 'mAppUpdate' and method 'updateApp'");
target.mAppUpdate = Utils.castView(view, R.id.app_upgrade, "field 'mAppUpdate'", TextView.class);
view2131296303 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.updateApp();
}
});
target.mCacheSize = Utils.findRequiredViewAsType(source, R.id.cache_size, "field 'mCacheSize'", TextView.class);
target.mToolbar = Utils.findRequiredViewAsType(source, R.id.toolbar, "field 'mToolbar'", Toolbar.class);
view = Utils.findRequiredView(source, R.id.app_clean, "method 'cleanCache'");
view2131296299 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.cleanCache();
}
});
view = Utils.findRequiredView(source, R.id.ic_launcher, "method 'setDevelopMode'");
view2131296479 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.setDevelopMode();
}
});
}

@Override
@CallSuper
public void unbind() {
AppSettingsActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;

target.mAppName = null;
target.mAppUpdate = null;
target.mCacheSize = null;
target.mToolbar = null;

view2131296303.setOnClickListener(null);
view2131296303 = null;
view2131296299.setOnClickListener(null);
view2131296299 = null;
view2131296479.setOnClickListener(null);
view2131296479 = null;
}
}


在Utils.findRequiredView中使用了findViewById方法,说明实际还是通过findViewById拿到View对象的

可借鉴学习的地方:(1)使用LinkedHashmap做缓存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值