到此我们整个的流程算分析完了。
最后我们看下对外提供的API
bind 方法
那么还差一步,什么时候都要我们生成的java文件呢?答案是:
ButterKnife.bind(this);方法。
我们看一下ButterKnife对外提供的API
/**
* 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);
}
/**
* 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 createBinding(target, target);
}
/**
* 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 createBinding(target, sourceView);
}
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link Activity} as the view root.
*
* @param target Target class for view binding.
* @param source Activity on which IDs will be looked up.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull Activity source) {
View sourceView = source.getWindow().getDecorView();
return createBinding(target, sourceView);
}
/**
* 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) {
return createBinding(target, source);
}
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link Dialog} as the view root.
*
* @param target Target class for view binding.
* @param source Dialog on which IDs will be looked up.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull Dialog source) {
View sourceView = source.getWindow().getDecorView();
return createBinding(target, sourceView);
}
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);
}
}
我们看到bind的一系列方法都会调用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,可以看类名就是我们生成的。
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 方法
在新版的8.4.0中去除了 unbind方法。
ButterKnife.unbind(this);
采用了接口的形式。让生成的类来实现,比如:
public final class SimpleAdapter$ViewHolder_ViewBinding implements Unbinder {
@UiThread
public SimpleAdapter$ViewHolder_ViewBinding(SimpleAdapter.ViewHolder target, View source) {
//...
}
//...
@Override
public void unbind() {
/...
}
}
那如何unbind呢??
ButterKnife.bind(this)返回值是一个我们生成的java文件类的实例对象。返回的是一个Unbinder 正和此意。
so,你可以这么用:
private Unbinder unbinder;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
unbinder=ButterKnife.bind(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
到此我们就算完了。
下一篇我们讲自己来实现类似butterknife的demo,这样我们才能正在的会学,会用。
下一篇 深入理解ButterKnife源码并掌握原理(五)