ButterKnife 源码探究

2020-12-01  本文已影响0人  peterXpq

1、 ButterKnife.bind()

绑定到 Activity
@NonNull @UiThread
  public static Unbinder bind(@NonNull Activity target) {
    // 通过target 获取到当前xml 布局
    View sourceView = target.getWindow().getDecorView();
    return bind(target, sourceView);
  }

绑定到 View

/**
   * BindView annotated fields and methods in the specified {@link View}. The view and its children
   * are used as the view root.
   *
   * @param target Target view for view binding.
   */
  @NonNull @UiThread
  public static Unbinder bind(@NonNull View target) {
    return bind(target, target);
  }

绑定到 Dialog

 /**
   * BindView annotated fields and methods in the specified {@link Dialog}. The current content
   * view is used as the view root.
   *
   * @param target Target dialog for view binding.
   */
  @NonNull @UiThread
  public static Unbinder bind(@NonNull Dialog target) {
    View sourceView = target.getWindow().getDecorView();
    return bind(target, sourceView);
  }

绑定到Fragment 上

/**
   * BindView annotated fields and methods in the specified {@code target} using the {@code source}
   * {@link View} as the view root.
   *
   * @param target Target class for view binding.
   * @param source View root on which IDs will be looked up.
   */
  @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());
    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);
    }
  }

bind 方法

/**
   * BindView annotated fields and methods in the specified {@code target} using the {@code source}
   * {@link View} as the view root.
   *
   * @param target Target class for view binding.
   * @param source View root on which IDs will be looked up.
   */
  @NonNull @UiThread
  public static Unbinder bind(@NonNull Object target, @NonNull View source) {
    // 通过 target 获取到类的实例
    Class<?> targetClass = target.getClass();
    if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
    // 通过class 查找 construtor 即编译生成的 classname_VViewBinding 类
    Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

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

    //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
    try {
      // 构造出一个 Unbinder
      return constructor.newInstance(target, source);
    }
    // code 
  }

看下 findBindingConstructorForClass 方法

2、根据class获取目标Class的包名+类名,根据拿到的包名+类名+_ViewBinding构造一个构造函数,然后传入targe和target.getWindow().getDecorView(),实例化构造函数。此时bind方法的任务就完成了
  //缓存集合
@VisibleForTesting
  static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();


@Nullable @CheckResult @UiThread
  private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
    // 如果缓存中有,就直接返回
    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;
    }
    // 获取当前类的类名,如果是系统名称,返回null
    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 {
      //获取cls的classloader并加载cls.getName()+_ViewBinding从而创建一个新类的Constructor的Class。而加载的这个新的类是项目在编译期间用注解编辑器生成的。
      Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
      //noinspection unchecked
      //从bindingClass类中获取到Constructor对象
      bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
    } 
    // code ellipsis ...
      //把新类放入缓存中下次使用,key=subscriber.getClass(),value=包名+subscriber类名_ViewBinding
    BINDINGS.put(cls, bindingCtor);
    return bindingCtor;
  }

看看 MainActivity_ViewBinding 这个类

public class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;

  private View view7f080433;

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

  @UiThread
  public MainActivity_ViewBinding(final MainActivity target, View source) {
    this.target = target;

    View view;
    target.noScrollViewPager = Utils.findRequiredViewAsType(source, R.id.noScrollViewPager, "field 'noScrollViewPager'", NoScrollViewPager.class);
    target.commonTabLayout = Utils.findRequiredViewAsType(source, R.id.commonTabLayout, "field 'commonTabLayout'", CommonTabLayout.class);
    view = Utils.findRequiredView(source, R.id.pop_img, "field 'popImg' and method 'onClick'");
    target.popImg = Utils.castView(view, R.id.pop_img, "field 'popImg'", ImageView.class);
    view7f080433 = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.onClick(p0);
      }
    });
  }

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

    target.noScrollViewPager = null;
    target.commonTabLayout = null;
    target.popImg = null;

    view7f080433.setOnClickListener(null);
    view7f080433 = null;
  }
}

3、其实ButterKnife 核心是 AnnotationProcessor(注解编译器) 和 JavaPoet,来生成ViewBinding 类....

上一篇下一篇

猜你喜欢

热点阅读