欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ButterKnife的一些见解

程序员文章站 2022-04-20 22:05:06
...

前言:

作为一个程序员,我很关心国家,希望中美贸易成功,国家更加开放。

本讲解的ButterKnife 是基于10.1版本的,其他版本源码一样。

ButterKnife用法很简单

  • 在Activity中
ButterKnife.bind(this);   //this 是Activity
  • 在fragment中

ButterKnife.bind(this,view);  //this是当前的fragment 对象,view当前View的布局

  • 自定义View

ButterKnife.bind(this,view);

或者

ButterKnife.bind(this);  

Activity的讲解

ButterKnife  是采用编译时注解的,所以在编译后会生成编译好的文件

public abstract class MainActivity <T extends BasePresenter> extends RxFragmentActivity {
   private Unbinder bind;
   public T mPresenter;
   public Context mContext;
   
   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      if (getLayoutId() != 0) {
         setContentView(getLayoutId());
      }
      bind = ButterKnife.bind(this);    //绑定

点击进入源码ButterKnife.bind

@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
  View sourceView = target.getWindow().getDecorView();           //获取当前Activity的布局的最高的父布局
  return bind(target, sourceView);         //调用ButterKnife的bind()方法
}

发现调用了ButterKnife 的bind(target,sourceView)

点击进入源码

步骤1

/**
 * 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();     //获取类对象(通过Class类对象可以获取类中许多信息,反射的原理)     
  if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
  Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); //传入类对象,生成一个Constructor

  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);
  }
}

发现源码中主要的代码为 findBindingConstructorForClass(targetClass)

步骤2

@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
  Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);    BINDINGS是一个LinkedHashMap这一步的作用是查看是否有Constructor对象,有的话直接返回,省的浪费资源   
  if (bindingCtor != null || BINDINGS.containsKey(cls)) {
    if (debug) Log.d(TAG, "HIT: Cached in binding map.");
    return bindingCtor;
  }
  String clsName = cls.getName();
  if (clsName.startsWith("android.") || clsName.startsWith("java.")   //判断是不是系统的类,如果是直接返回null
      || clsName.startsWith("androidx.")) {
    if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
    return null;
  }
  try {
    Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");  //根据当前类重新生成一个类名为  class.name + _ViewBinding的类名的类对象(clsName + "_ViewBinding"  这个类在编译的时候就已经存在了,编译注解)
//关于如何获取类对象这里有三个方法
//方法一: 
Class a = 对象.getClass() 
//方法二:
Class b = 类.class;
//方法三:
Class c = Class.forName(String ClassName); 
//方法四:(不建议使用)
Class d = context.getClassLoader().loadClass(String ClassName)
    //noinspection unchecked
    bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
   //最后用新的 Class 创建一个 继承了Unbinder的 Constructor,并添加到BINDINGS
:
    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); //将新生成的对象添加到BINDINGS
  return bindingCtor;
}

返回到步骤1中

发现最终返回的是

所以最终bind()方法返回的是MainActivity_ViewBinding类的实例

MainActivity_ViewBinding的实例,那MainActivity_ViewBinding这个类肯定是存在的。可以在如下目录找到它

ButterKnife的一些见解

 

我们看下这个类中做了什么操作

public class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;

  private View view7f07004c;

  @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;
    view = Utils.findRequiredView(source, R.id.img, "field 'img' and method 'onViewClicked'");
    target.img = Utils.castView(view, R.id.img, "field 'img'", ImageView.class);
    view7f07004c = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.onViewClicked();
      }
    });
  }

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

    target.img = null;

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

这类中持有了MainActivity的引用,所以无论如何在Activity销毁的时候一定要解绑

自定义VIew该如何解绑呢?

ButterKnife的一些见解

public MyView(Context context, @Nullable AttributeSet attrs) {
   this(context, attrs, 0);
   View inflate = LayoutInflater.from(context).inflate(R.layout.myview, this, true);
   bind = ButterKnife.bind(this,inflate);   
   bind = ButterKnife.bind(inflate);//这个两个方法的效果是一样的,可以查看源码,单个参数的实际是调用了两个参数的

借鉴文章:

ButterKnife 原理解析

ButterKnife的源码解析