一 運行期java
咱們在activity中的onCreate方法中會調用ButterKnife.bind(this);咱們進入這個方法:android
public static void bind(Activity target) {ide
bind(target, target, Finder.ACTIVITY);ui
}this
target是這個activity,再進入bind方法,spa
static void bind(Object target, Object source, Finder finder) {debug
Class<?> targetClass = target.getClass();code
try {orm
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等也同樣,最底層就是調用了咱們平時獲取資源的方法。