一 运行期
我们在activity中的onCreate方法中会调用ButterKnife.bind(this);我们进入这个方法:
public static void bind(Activity target) {
bind(target, target, Finder.ACTIVITY);
}
target是这个activity,再进入bind方法,
static void bind(Object target, Object source, Finder finder) {
Class<?> targetClass = target.getClass();
try {
if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());
1 ViewBinder<Object> viewBinder = findViewBinderForClass(targetClass);
if (viewBinder != null) {
2 viewBinder.bind(finder, target, source);
}
} catch (Exception e) {
throw new RuntimeException("Unable to bind views for " + targetClass.getName(), e);
}
}
核心是1,2行代码,找到ViewBinder,然后调用它的bind方法。bind()方法的3个参数:sourrce和targetClass就是activity,finder是Finder.ACTIVITY,finder比较重要,调用他的内部方法来获取资源,然后进入findViewBinderForClass方法查看
private static ViewBinder<Object> findViewBinderForClass(Class<?> cls)
throws IllegalAccessException, InstantiationException {
ViewBinder<Object> viewBinder = BINDERS.get(cls);
if (viewBinder != null) {
if (debug) Log.d(TAG, "HIT: Cached in view binder map.");
return viewBinder;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return NOP_VIEW_BINDER;
}
try {
1 Class<?> viewBindingClass = Class.forName(clsName + "$$ViewBinder");
//noinspection unchecked
2 viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();
if (debug) Log.d(TAG, "HIT: Loaded view binder class.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
viewBinder = findViewBinderForClass(cls.getSuperclass());
}
BINDERS.put(cls, viewBinder);
return viewBinder;
}
核心代码是1,2根据简单反射的原理,就是构造了一个类的实例,这个类是什么呢,根据前面所知cls即为activity,我们在MainActivity用注解Bind,那cls就是MainActivity,
clsName + "$$ViewBinder"
拼接的字符串即为Maintivity$$ViewBinder,正好是在编译期创建的那个源文件的类名,执行2之后,我们已获得了这个类的实例。切回到
static void bind(Object target, Object source, Finder finder)
这个方法中2行代码,就是调用了源文件类所对应类实例中的bind方法。我们再次看这个源文件bind方法中做了什么。
public class MainActivity$$ViewBinder<T extends com.hsj.weather.ui.activity.MainActivity> implements ViewBinder<T> {
@Override public void bind(final Finder finder, final T target, Object source) {
View view;
1 view = finder.findRequiredView(source, 2131361899, "field 'iv_title_left'");
2 target.iv_title_left = finder.castView(view, 2131361899, "field 'iv_title_left'");
view = finder.findRequiredView(source, 2131361901, "field 'iv_title_right'");
target.iv_title_right = finder.castView(view, 2131361901, "field 'iv_title_right'");
view = finder.findRequiredView(source, 2131361900, "field 'tv_title_center'");
target.tv_title_center = finder.castView(view, 2131361900, "field 'tv_title_center'");
}
@Override public void unbind(T target) {
target.iv_title_left = null;
target.iv_title_right = null;
target.tv_title_center = null;
}
}
我们看1,2对应的代码即可
先看1,finder调用了findRequiredView(),这个Finder是ButterKnife的一个枚举字段。这个finder的代码贴一下比较重要,
public enum Finder {
VIEW {
@Override protected View findView(Object source, int id) {
return ((View) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return ((View) source).getContext();
}
@Override protected String getResourceEntryName(Object source, int id) {
final View view = (View) source;
// In edit mode, getResourceEntryName() is unsupported due to use of BridgeResources
if (view.isInEditMode()) {
return "<unavailable while editing>";
}
return super.getResourceEntryName(source, id);
}
},
ACTIVITY {
@Override protected View findView(Object source, int id) {
return ((Activity) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return (Activity) source;
}
},
DIALOG {
@Override protected View findView(Object source, int id) {
return ((Dialog) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return ((Dialog) source).getContext();
}
};
private static <T> T[] filterNull(T[] views) {
int end = 0;
for (int i = 0; i < views.length; i++) {
T view = views[i];
if (view != null) {
views[end++] = view;
}
}
return Arrays.copyOfRange(views, 0, end);
}
@SafeVarargs
public static <T> T[] arrayOf(T... views) {
return filterNull(views);
}
@SafeVarargs
public static <T> List<T> listOf(T... views) {
return new ImmutableList<>(filterNull(views));
}
public <T> T findRequiredView(Object source, int id, String who) {
T view = findOptionalView(source, id, who);
if (view == null) {
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' annotation.");
}
return view;
}
public <T> T findOptionalView(Object source, int id, String who) {
View view = findView(source, id);
return castView(view, id, who);
}
@SuppressWarnings("unchecked") // That's the point.
public <T> T castView(View view, int id, String who) {
try {
return (T) view;
} catch (ClassCastException e) {
if (who == null) {
throw new AssertionError();
}
String name = getResourceEntryName(view, id);
throw new IllegalStateException("View '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was of the wrong type. See cause for more info.", e);
}
}
@SuppressWarnings("unchecked") // That's the point.
public <T> T castParam(Object value, String from, int fromPosition, String to, int toPosition) {
try {
return (T) value;
} catch (ClassCastException e) {
throw new IllegalStateException("Parameter #"
+ (fromPosition + 1)
+ " of method '"
+ from
+ "' was of the wrong type for parameter #"
+ (toPosition + 1)
+ " of method '"
+ to
+ "'. See cause for more info.", e);
}
}
查看findRequiredView()方法(根据前面编译期创建源码时知道,根据前面分析所知这个Finder类型是Finder.ACTIVITY) ,故T为Finder.ACTIVITY:
public <T> T findRequiredView(Object source, int id, String who) {
T view = findOptionalView(source, id, who);
if (view == null) {
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' annotation.");
}
return view;
}
进入findOptionalView方法,
public <T> T findOptionalView(Object source, int id, String who) {
View view = findView(source, id);
return castView(view, id, who);
}
进入findView方法:
protected abstract View findView(Object source, int id);
发现是其抽象方法,谁实现了它呢,在看Finder枚举
VIEW {
@Override protected View findView(Object source, int id) {
return ((View) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return ((View) source).getContext();
}
@Override protected String getResourceEntryName(Object source, int id) {
final View view = (View) source;
// In edit mode, getResourceEntryName() is unsupported due to use of BridgeResources
if (view.isInEditMode()) {
return "<unavailable while editing>";
}
return super.getResourceEntryName(source, id);
}
},
ACTIVITY {
@Override protected View findView(Object source, int id) {
return ((Activity) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return (Activity) source;
}
},
DIALOG {
@Override protected View findView(Object source, int id) {
return ((Dialog) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return ((Dialog) source).getContext();
}
};
因为那个T是Finder.ACTIVITY,所以会调用Finder.ACTIVITY的findView方法,方法里看到一句写烂的一句代码:
((Activity) source).findViewById(id);
就是用了findViewById来获取那个View。至此切换到那个MainActivity$$ViewBinder的Bind方法
@Override public void bind(final Finder finder, final T target, Object source) {
View view;
view = finder.findRequiredView(source, 2131361899, "field 'iv_title_left'");
1 target.iv_title_left = finder.castView(view, 2131361899, "field 'iv_title_left'");
view = finder.findRequiredView(source, 2131361901, "field 'iv_title_right'");
target.iv_title_right = finder.castView(view, 2131361901, "field 'iv_title_right'");
view = finder.findRequiredView(source, 2131361900, "field 'tv_title_center'");
target.tv_title_center = finder.castView(view, 2131361900, "field 'tv_title_center'");
}
target就是MainActivity的实例,执行完在MainActivity中的变量iv_title_left就被赋值了,在这个activity中就可以直接使用iv_title_left了,比如。
iv_title_left.setImageResource(R.drawable.btn_addcity_normal);
在activity中我们重写了onDestroy:
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
ButterKnife.unbind(this);
}
ButterKnife.unBind()的调用,我们可以猜到内部就是调用了,那个源文件类中的unBind()方法。
而在unBind()方法中,就是将这些变量赋值为null。释放掉内存。
进过系列分析,可以得出它的原理运行图:
ButterKnife在编译期和运行期的运作机制原理差不多讲到这里了,对其他注解元素,诸如BindBitmap,BindDimen等也一样,最底层就是调用了我们平时获取资源的方法。