ButterKnife使用及原理

一、ButterKnife使用

1、在build.gradle的dependencies添加类库

implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

2、初始化绑定以Activity为例,其他类同。

unbinder =ButterKnife.bind(this);

3、对各个元素进行绑定

//绑定单个view
@BindView(R.id.title_tv) TextView titleTv;
//绑定字符串
@BindString(R.string.bind_string) String bindString;
//绑定Color
@BindColor(R.color.red) int red;
//绑定Dimen
@BindDimen(R.dimen.bind_dimen) float bindDimen; // dip * 屏幕密度
//绑定Drawable
@BindDrawable(R.mipmap.sky)   Drawable bindDrawable;
//绑定多个view
@BindViews({R.id.tv_1,R.id.tv_2,R.id.tv_3}) List<TextView> textTvs;
//绑定点击事情
@OnClick({R.id.onclick_tv})
public void onClick(View view){
    switch (view.getId()){
        case R.id.onclick_tv:
            Toast.makeText(ActivityButterKnife.this,"点击事件绑定",Toast.LENGTH_SHORT).show();
            break;

    }

}

4、对事件,View等进行解绑

unbinder.unbind();

二、ButterKnife原理分析

  1. ButterKnife原理--创建Class_ViewBinding.class,需要三步

安卓编译时注解,在安卓编译时遍历注解,注解部分生成java类,保存在相同目录下。

        (1)在安卓编译时遍历注解,对各个类型注解处理。

        (2)注解部分生成java类

        (3)保存在相同目录下

@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
  Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);

  for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
    TypeElement typeElement = entry.getKey();
    BindingSet binding = entry.getValue();

    JavaFile javaFile = binding.brewJava(sdk, debuggable, useAndroidX);
    try {
      javaFile.writeTo(filer);
    } catch (IOException e) {
      error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
    }
  }

  return false;
}

 

     2.ButterKnife原理获取 Class_ViewBinding.class

(1)ButterKnife.bind(this) 相当于 new Class_ViewBinding(this)

(2)Activity为例:获取View,创建Class的绑定实例

@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull Activity source) {
  View sourceView = source.getWindow().getDecorView();
  return createBinding(target, sourceView);
}

 (3)通过反射获取构造函数

@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
 //根据ClassName从缓存中获取构造函数

 Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
  if (bindingCtor != null) {
    if (debug) Log.d(TAG, "HIT: Cached in binding map.");
    return bindingCtor;
  }

//不搜寻,系统类

 String clsName = cls.getName();
  if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
    if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
    return null;
  }
  try {

//如果没有,拿到Class_ViewBinding字节码,获取构造函数
    Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
    //noinspection unchecked
    bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
    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());

//找不Class_ViewBinding,一直遍历父类,直到遇到(android 或java开头包名的类)
    bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
  } catch (NoSuchMethodException e) {
    throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
  }
  BINDINGS.put(cls, bindingCtor);
  return bindingCtor;
}

 

(4)利用构造函数实例化Class_ViewBinding对象

private static Unbinder createBinding(@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);
  }
}

 

 

 

 

猜你喜欢

转载自blog.csdn.net/u011638706/article/details/81436466