美文网首页
ButterKnife 源码探究

ButterKnife 源码探究

作者: peterXpq | 来源:发表于2020-12-01 16:31 被阅读0次

    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;
      }
    }
    
    • 本质上还是通过findViewById 查找view的,只是这部分代码不需要我们自己写了而已,框架在编译时会自动帮我们生成好了。

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

    相关文章

      网友评论

          本文标题:ButterKnife 源码探究

          本文链接:https://www.haomeiwen.com/subject/mpeswktx.html