本篇文章我们来看看ButterKnife对外提供的API。经过前面的分析,我们已经生成了java代码,那么这些方法怎样调用呢?
实际上就是通过ButterKnife.bind()调用;那就来看看这个方法。
bind()
/**
* BindView annotated fields and methods in the specified {@link Activity}. The current content
* view is used as the view root.
*
* @param target Target activity for view binding.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
这个方法又调用了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);
}
}
Class<\?> targetClass = target.getClass();获取类的实例,最后获取构造函数,最后constructor.newInstance方法来调用该类的构造函数。 而该类的构造函数是通过findBindingConstructorForClass方法。
@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, 这里可以看到使用的类名就是在收集信息时生成的辅助类,比如MainActivity_ViewBinding
Class<?> bindingClass = Class.forName(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;
}
unbind()
ButterKnife在8.4.0版本之后去除了unbind方法,而采用了接口的形式,旨在让生成的类来实现,举个例子:
public final class SimpleAdapter$ViewHolder_ViewBinding implements Unbinder {
@UiThread
public SimpleAdapter$ViewHolder_ViewBinding(SimpleAdapter.ViewHolder target, View source) {
//...
}
@Override
public void unbind() {
//...
}
}
该如何解绑呢? 我们发现bind方法返回了Unbinder, 完全可以借助于此。
到此为止,ButterKnife源码解析就告一段落了,懂的了它的原理是一方面,能把这种原理运用于实践变成自己知识网络中的一个节点才是主要目的;下一篇我们将通过demo展示ButterKnife的原理。