Butterknife

135 阅读2分钟

工作原理

  1. 运用APT技术,自定义注解,编译是扫描注解,解析注解生成对应java代码。

  2. 调用ButterKnife.bind(this)方法的时候,将ID与对应的上下文绑定在一起。

源码解析

public static Unbinder bind(@NonNull Object target, @NonNull Dialog source) {
  View sourceView = source.getWindow().getDecorView();
  return bind(target, sourceView);
}
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
  Class<?> targetClass = target.getClass();
  if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
  (1)重点关注这个方法:去寻找一个构造函数
  Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

  if (constructor == null) {
    return Unbinder.EMPTY;
  }

  //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
  try {
  (2)通过构造函数生成对象
    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);
  }
}
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
  (1)先从map中找,找到直接返回
  Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
  if (bindingCtor != null || BINDINGS.containsKey(cls)) {
    if (debug) Log.d(TAG, "HIT: Cached in binding map.");
    return bindingCtor;
  }
  (2)系统类直接返回
  String clsName = cls.getName();
  if (clsName.startsWith("android.") || clsName.startsWith("java.")
      || clsName.startsWith("androidx.")) {
    if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
    return null;
  }
  try {
    (3)加载APT生成的类
    Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
    //noinspection unchecked4)拿到类的构造函数
    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());
    (5)没找到从父类里面找
    bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
  } catch (NoSuchMethodException e) {
    throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
  }
  BINDINGS.put(cls, bindingCtor);
  return bindingCtor;
}
public class APTActivity_ViewBinding implements Unbinder {
  private APTActivity target;

  private View view7f0801ea;

  private View view7f080065;

  @UiThread
  public APTActivity_ViewBinding(APTActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public APTActivity_ViewBinding(final APTActivity target, View source) {
    this.target = target;

    View view;
    view = Utils.findRequiredView(source, R.id.tv, "field 'name' and method 'submit01'");
    target.name = Utils.castView(view, R.id.tv, "field 'name'", TextView.class);
    view7f0801ea = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.submit01();
      }
    });
    view = Utils.findRequiredView(source, R.id.btn, "field 'btn' and method 'submit'");
    target.btn = Utils.castView(view, R.id.btn, "field 'btn'", Button.class);
    view7f080065 = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.submit();
      }
    });
  }

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

    target.name = null;
    target.btn = null;

    view7f0801ea.setOnClickListener(null);
    view7f0801ea = null;
    view7f080065.setOnClickListener(null);
    view7f080065 = null;
  }
}

总结:

  1. 通过APT生成对应的类,在里面做了findViewById。
  2. 调用ButterKnife.bind(this)完成APT生成类的类加载。并通过反射生成该类,在该类的构造函数里面调用了findViewById。