编译时注解 - ButterKnife源码分析

其实当我们每次点击运行的时候, 都会去扫描运行时的注解,然后自动生成这么一个类,是自动生成的不是我们自己写的。

xxx_ViewBinding 作为类名,实现 Unbinder 在 xxx_ViewBinding 的构造函数里面去 findViewById 或者 setOnclickListener。我们只要在 MainActivity 中去实例化一个 MainActivity_ViewBinding 对象,那么我们 MainActivity 里面的所有属性就都会被赋值了。

ButterKnife.bind(this);


@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
    View sourceView = target.getWindow().getDecorView();
    return createBinding(target, sourceView);
}

target.getWindow().getDecorView();语句获取activity的根视图decorView。读过view源码的同学知道,activity的根view为叫mDecor的Framlayout,也就是这里获取到的sourceView。

获取到sourceView后,将target(就是XXXActivity对象)与sourceView(XXXActivity的根视图)作为参数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);
}

}

这里只关心:

Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

通过XXXActivity.class获取对应的构造器,看到这里我们可以猜到是通过反射创建Unbinder实例。
果然紧接着就是

return constructor.newInstance(target, source);

先看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<?> bindingClass = cls.getClassLoader().loadClass(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;

}

这个方法的作用就是利用XXXActivity.class的类名,找到名叫XXXActivity_ViewBinding的类,并创建一个实例。

这个XXXActivity_ViewBinding类是自动生成的,实现implements Unbinder接口

例如:

public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;

@UiThread
public MainActivity_ViewBinding(MainActivity target) {
    this(target, target.getWindow().getDecorView());
}

@UiThread
public MainActivity_ViewBinding(MainActivity target, View source) {
    this.target = target;
    //这里因为xml布局只有一个:TextView:id:tv_content,变量名:tvContent,如果有多个,则创建多个
    target.tvContent = Utils.findRequiredViewAsType(source, R.id.tv_content, "field 'tvContent'", TextView.class);
}

@Override
@CallSuper
public void unbind() {
    MainActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
        this.target = null;

        target.tvContent = null;
    }
}

这个类有两个构造方法,和一个unbind函数。我们先看构造方法。不知道大家还记得么,构造方法的两个参数:target(xxxActivity) , source (xxxActivity的根视图)。 构造方法只有一个方法findRequiredViewAsType,我们看下这个方法做了什么:

 public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
  Class<T> cls) {
    View view = findRequiredView(source, id, who);
    return castView(view, id, who, cls);
 }

先看findRequiredView:

public static View findRequiredView(View source, @IdRes int id, String who) {
//终于找到了....
View view = source.findViewById(id);
if (view != null) {
  return view;
}
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' (fields) or '@Optional'"
    + " (methods) annotation.");
 }

终于找到了findViewById。。。。可以看到,函数的返回值是View,我们需要的是TextView,所以第二个函数castView出场了,这个函数的作用就是类型强制转换。

到这里其实就差不多了。。。
谢谢阅读,我这里也是学习的态度在这里分享,有什么问题希望大家能提出来能提出来,随时可以和我交流探讨:QQ:707086125 微信:loveme_dp

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容