ButterKnife 源碼探究

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 {
      // 構(gòu)造出一個 Unbinder
      return constructor.newInstance(target, source);
    }
    // code 
  }

看下 findBindingConstructorForClass 方法

2、根據(jù)class獲取目標Class的包名+類名,根據(jù)拿到的包名+類名+_ViewBinding構(gòu)造一個構(gòu)造函數(shù),然后傳入targe和target.getWindow().getDecorView(),實例化構(gòu)造函數(shù)。此時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;
    }
    // 獲取當前類的類名,如果是系統(tǒng)名稱,返回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;
    }
    // 如果緩存中沒有,就直接創(chuàng)建一個
    try {
      //獲取cls的classloader并加載cls.getName()+_ViewBinding從而創(chuàng)建一個新類的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;
  }
}
  • 本質(zhì)上還是通過findViewById 查找view的,只是這部分代碼不需要我們自己寫了而已,框架在編譯時會自動幫我們生成好了。

3、其實ButterKnife 核心是 AnnotationProcessor(注解編譯器) 和 JavaPoet,來生成ViewBinding 類....

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • ButterKnife 算是一款知名老牌 Android 開發(fā)框架了,通過注解綁定視圖,避免了 findViewB...
    SheHuan閱讀 21,618評論 2 69
  • 前言 Jake Wharton大神的Butterknife可謂是造福廣大Android開發(fā)者, 再也不用重復寫fi...
    海之韻Baby閱讀 861評論 0 0
  • butterknife注解框架相信很多同學都在用,但是你真的了解它的實現(xiàn)原理嗎?那么今天讓我們來看看它到底是怎么實...
    打不死的小強qz閱讀 723評論 0 4
  • 前言 本篇是系列文章的第四篇,Butterknife源碼全面解析。上一篇利用反射和注解手擼一個Android依賴注...
    肖邦kaka閱讀 1,264評論 0 8
  • ButterKnife源碼分析 1. ButterKnife 簡介 1.1 代碼結(jié)構(gòu) butterknife ;a...
    海南雞閱讀 422評論 0 0

友情鏈接更多精彩內(nèi)容