编译时注解 - 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 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 constructor = findBindingConstructorForClass(targetClass);

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

return constructor.newInstance(target, source);

先看findBindingConstructorForClass方法:

@Nullable @CheckResult @UiThread
 private static Constructor findBindingConstructorForClass(Class cls) {
Constructor 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) 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 findRequiredViewAsType(View source, @IdRes int id, String who,
  Class 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

你可能感兴趣的:(编译时注解 - ButterKnife源码分析)