ButterKnife的一些见解

前言:

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

本讲解的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这个类肯定是存在的。可以在如下目录找到它

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

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该如何解绑呢?

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的源码解析

猜你喜欢

转载自blog.csdn.net/xueyoubangbang/article/details/89959442