标签:
一 运行期
我们在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方法。targetClass就是activity对应的class,然后进入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等也一样,最底层就是调用了我们平时获取资源的方法。
标签:
原文地址:http://my.oschina.net/u/1463920/blog/512784