「ButterKnife 源码分析」看这一篇就够了!

1870221-df2120ae95c23303.png

「ButterKnife」是 Android 系统的 View 注入框架,极大的便利了我们的开发,对应用的性能基本没有影响,是我们开发中一把尖锐的匕首。<br /><br /><br />-- Mr.S

分析源码之前

ButterKnife 大神的 github ,最好的老师。

一、使用

使用的版本是 8.8.1 并不是最新的,因为最新的适配 androidx,会有一些问题,功能是一样的。

1、配置
dependencies {
    ......
    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
buildscript {
    
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        //这一句
        classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'
    }
}

如果在 library 中 ,记得添加下面的代码。

apply plugin: 'com.jakewharton.butterknife'
2、简单使用

更多具体使用,参见其他博客。

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.button)
    Button button;

    @BindView(R.id.button2)
    Button button2;

    @BindView(R.id.textView)
    TextView textView;

    @OnClick(R.id.button)
    public void showToast(Button button) {

        Toast.makeText(this, "show_button", Toast.LENGTH_SHORT).show();
        textView.setText("woahhahaha");
        button.setText("woahhahaha");

    }

    @OnClick(R.id.textView)
    public void showToast() {

        Toast.makeText(this, "show_text", Toast.LENGTH_SHORT).show();

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ButterKnife.bind(this);

    }
}

二、使用须知

1、三大绑定情景
  • 在Activity中绑定ButterKnife
  • 在Fragment中绑定ButterKnife
  • 在Adapter中绑定ButterKnife
2、丰富的功能
  • 绑定View
  • 绑定资源
  • 事件绑定
  • 绑定监听
  • 使用findById
  • 设置多个view的属性
  • 更多事件注解

分析源码

一、看的见的源码

1、bind

我们发现肉眼可见的只有一个方法,一个框架能封装的如此简单,真是神一般的存在了。

 ButterKnife.bind(this);

可以看出只在 UI 线程中执行。创建 目标对象 和 视图 的绑定。sourceView 就是我们的 DecorView。可以把这个Activity 可见得所有 view 都遍历到。

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

我们用 activity 代替目标对象,这样往下分析的时候大家可以清晰一点。

我们发现通过获得 activity 的 class 对象,然后通过 findBindingConstructorForClass 反射获得 Unbinder 的子类的构造方法,最后通过 constructor.newInstance(target, source) 反射获得 Unbinder 子类的对象。自此绑定结束。那么重点还是 findBindingConstructorForClass 方法。

  private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
    //首先获得目标对象的 class 对象 ps:看到这个就知道要用反射了
    Class<?> targetClass = target.getClass();
    if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
    //我们用 activity 代替目标对象,这样往下分析的时候大家可以清晰一点
    //
    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);
    }
  }
  @Nullable @CheckResult @UiThread
  private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
    //BINDINGS Map 先从 BINDINGS 中根据 class 对象 查找相应的 Unbinder 子类对象 有的话直接返回
    //static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();
    Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
    if (bindingCtor != null) {
      if (debug) Log.d(TAG, "HIT: Cached in binding map.");
      return bindingCtor;
    }
    //获取 activity  的类名
    String clsName = cls.getName();
    //如果是 android 和 java 开头 那么直接返回 null 这些是 framework  类
    if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
      if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
      return null;
    }
    try {
      //这里就是是 “com.ssy.MainActivity_ViewBinding” 我们通过类加载器加载这个类
      //为何会有这个类呢?我们下面去分析 这里写 略过
      Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
      //noinspection unchecked
      //反射得到这个类的的构造方法 最后返回这个 bindingCtor 这个构造方法
      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());
      bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
    } catch (NoSuchMethodException e) {
      throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
    }
    //加入 map 缓存
    BINDINGS.put(cls, bindingCtor);
    return bindingCtor;
  }

我们来整理一下,我们得到找到一个 类似 MainActivity 类的 MainActivity_ViewBinding ,然后调用了这个类的构造方法,获得了这个类的实例。这样前不着村,后不着店的,很是懵逼啊。

2、xxx_ViewBinding

唯一的线索就是 MainActivity_ViewBinding 类了,那么我们就查找一下这个类。这是 ButterKnife 自动生成的,我们之后在讨论这个类是如何生成的。<br />MainActivity_ViewBinding 实现了 Unbinder。

只有一个解除绑定的方法。

public interface Unbinder {
  @UiThread void unbind();

  Unbinder EMPTY = new Unbinder() {
    @Override public void unbind() { }
  };
}

注意:button 和 textView 都有监听事件 button2 没有任何事件。然后我们看下面代码的描述。

public class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;
//这个是 button 用于 解绑 
  private View view2131165218;
//这个是 textView 用于 解绑 
  private View view2131165321;

  @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;
    // 实际通过  source.findViewById 获得这个 view 
    //后面 who 字符串 用于 这个 view 的描述 变量名 :button  关联的监听事件方执行的方法是 showToast
    view = Utils.findRequiredView(source, R.id.button, "field 'button' and method 'showToast'");
   //因为这个 view 是 View 所以强转成具体 类 比如 Button 然后 赋值给  target.button 
    //这个其实就是引用 和 具体 view 的绑定
    target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
    //把 这个 局部变量view 赋值给 全局变量 view2131165218  用于 解绑的操作
    view2131165218 = view;
    //然年后 设置监听事件 调用 target 的 showToast 方法,参数是这个 点击事件的 view
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.showToast(Utils.castParam(p0, "doClick", 0, "showToast", 0, Button.class));
      }
    });
    //同理 获得 button2 但是这个么有 onClick 事件 所以就省略了 上面的代码
    target.button2 = Utils.findRequiredViewAsType(source, R.id.button2, "field 'button2'", Button.class);
    //这个也是同理 button
    view = Utils.findRequiredView(source, R.id.textView, "field 'textView' and method 'showToast'");
    target.textView = Utils.castView(view, R.id.textView, "field 'textView'", TextView.class);
    view2131165321 = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        //区别是 这个 showToast 是无参数的
        target.showToast();
      }
    });
  }
//最后是解绑 上面的 view
  @Override
  @CallSuper
  public void unbind() {
    MainActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.button = null;
    target.button2 = null;
    target.textView = null;

    view2131165218.setOnClickListener(null);
    view2131165218 = null;
    view2131165321.setOnClickListener(null);
    view2131165321 = null;
  }
}

我们通过这个生成类,发现它在 bind 方法内替我们做了我们嫌麻烦的事情,代码也很简单,如果我们用静态类的方法去实现的话,很简单,但是 ButterKnife 是动态生成。注意是编译器生成的,所以不影响应用的速度,如果是运行时的解释执行肯定会影响应用速度的。这就是运行时注解和编译时注解的区别了。

那么这个究竟是怎么生成的这个类呢?那就涉及到注解处理器了。

二、语言模型包的使用

1、Mirror<br />注解处理器因为操作的是源码,所以需要用到JAVA语言模型包,javax.lang.model及其子包都是Java的语言模型包。这个包是采用了Mirror设计,Java是一种可以自描述的语言,其反射机制就是一种自描述,传统的反射机制将自描述与其他操作合并在一起,Mirror机制将自描述跟其他操作隔离,自描述部分是Meta level,其他部分是Base level

[图片上传失败...(image-1b24e6-1549882808300)]

2、Element:<br />代表语言元素,比如包,类,方法等,但是Element并没有包含自身的信息,自身信息要通过Mirror来获取,每个Element都指向一个TypeMirror,这个TypeMirror里有自身的信息。通过下面获方法取Element中的Mirror

TypeMirror mirror=element.asType() 

3、TypeMirror<br />TypeMirror类型是DeclaredType或者TypeVariable时候可以转化成Element

Types types=processingEnvironment.getTypeUtils()
Element element=types.asElement(typeMirror)

4、获取类型

获取Element或者TypeMirror的类型都是通过getKind()获取类型,但是返回值虽然不同,但是都是枚举。<br /><br />

ElementKind element.getKind()
TypeKind typeMirror.getKind()

对具体类型,要避免用instanceof,因为即使同一个类getKind()也有不同的结果。比如TypeElemnt的getKind()返回结果可以是枚举,类,注解,接口四种。<br /><br /><br />5、源码中移动

比如一个类里面有方法,成员变量等,这个类相对于方法跟成员变量就是外层元素,而成员变量和方法相对于类就是内层元素。 <br />Element是代表源码的类,源码中移动到其他位置必须是用Element,比如移动到当前元素的外层元素TypeElement。<br /><br />

public static TypeElement findEnclosingTypeElement( Element e ){
       while( e != null && !(e instanceof TypeElement) )       {
           e = e.getEnclosingElement();//通过getEnclosingElement()获取外层元素
       }
       return TypeElement.class.cast( e );
}

也可以使用getEnclosedElements()获取当前Element内层的所有元素。<br /><br /><br />6、类继承树结构中移动

TypeMirror是用来反应类本身信息的类,在继承树移动必须用到TypeMirror,比如查找某个类的父类

Types typeUtils = processingEnv.getTypeUtils();
while (!typeElement.toString().equals(Object.class.getName())) {
        TypeMirror typeMirror=element.getSuperclass();
        element = (TypeElement)typeUtils.asElement(typeMirror);
}

7、处理MirroredTypeException

编译器使用注解处理器来处理目标代码时候,目标源码还没有编译成字节码,所以任何指向目标源码字节码的代码都会发生MirroredTypeException。最常见的例子见下面

//已经编译的三方jar:
@Retention(RetentionPolicy.SOURCE)
@interface What{
    Class<?> value()
}
//源码:
@What(A.class)
public class A{}
//注解处理器
public ProcessorA extends AbstractProcessor{
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        ...
        What what=typeElementA.getAnnotation(What.class);
        Class<?> clazz=what.value();  //这里将有MirroredTypeException
        ...
    }
}

注解处理器之所有MirroredTypeException,是因为此时类A还没有被编译成字节码,所以A.class不存在,解决这个问题需要异常,利用异常我能获取类A的名字等信息,但是却得不到A.class,如下

public ProcessorA extends AbstractProcessor{
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        ...
        What what=typeElementA.getAnnotation(What.class);
        try {
            Class<?> clazz=what.value();  //这里将有MirroredTypeException
        }catch(MirroredTypeException mte){
            TypeMirror classTypeMirror =  mte.getTypeMirror();
            TypeElement classTypeElement = (TypeElement)Types.asElement(classTypeMirro);
            //获取canonicalName
            String canonicalName= classTypeElement.getQualifiedName().toString();
            //获取simple name
            String simpleName = classTypeElement.getSimpleName().toString();
        }
        ...
    }
}

三、看不见的「注解处理器」

关于 注解处理器 可以看这篇文章

不得不说之「注解」

所以我们只要根据 APT 和 JavaPoet 的规则来生成 「MainActivity_ViewBinding」 就好了。

那么源码里是如何做的呢?我们抽取一部分来看一下。

我们要想生成 注入的 Java 代码 必须在这里处理

1、process
  @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();
//遍历 执行 binding  的 brewJava 又是什么意思呢? 跳转 4
      JavaFile javaFile = binding.brewJava(sdk, debuggable);
      try {
        //最后 写入 Java 文件中 这个我们是清楚的
        javaFile.writeTo(filer);
      } catch (IOException e) {
        error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
      }
    }

    return false;
  }

带着疑问 继续往下看。

2、findAndParseTargets

查找并解析所有的注解目标

    
 private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
   //首先 是 Map 这个很重要 以 类型元素 为 key  保存 BindingSet.Builder 具体我们再接着往下看
    Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
   //被擦除的目标名称 集合
    Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
   ···
     
     //我们只需要看绑定 BindView 就好了 其他的都差不多 获得
     //getElementsAnnotatedWith 查找到 所有使用 BindView 注解 的元素
     // Process each @BindView element.
    for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
      // we don't SuperficialValidation.validateElement(element)
      // so that an unresolved View type can be generated by later processing rounds
      try {
      //解析 我们 BindView 元素 我们看看 是怎么 处理 builderMap 和  erasedTargetNames 
      //跳转 3  
        parseBindView(element, builderMap, erasedTargetNames);
      } catch (Exception e) {
        logParsingError(element, BindView.class, e);
      }
    }
     
     
   
   ···
// Associate superclass binders with their subclass binders. This is a queue-based tree walk
    // which starts at the roots (superclasses) and walks to the leafs (subclasses).
   //将 builderMap 中的数据 添加到 队列中
     Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
        new ArrayDeque<>(builderMap.entrySet());
    Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
   //把 擦除的目标 从 Set 里剔除
    while (!entries.isEmpty()) {
      //出列
      Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();

      TypeElement type = entry.getKey();
      BindingSet.Builder builder = entry.getValue();
//是否 是 擦除的目标名字
      TypeElement parentType = findParentType(type, erasedTargetNames);
      //如果没有父类 那么直接保存 type 和对应的额BindSet
      if (parentType == null) {
        bindingMap.put(type, builder.build());
      } else {
        //如果有父元素  那么从 bindingMap 查看是否有
        BindingSet parentBinding = bindingMap.get(parentType);
        //如果有 bindingMap  则给它设置 父 bindingMap
        if (parentBinding != null) {
          builder.setParent(parentBinding);
          bindingMap.put(type, builder.build());
        } else {
          //如果没有 再次入队列
          // Has a superclass binding but we haven't built it yet. Re-enqueue for later.
          entries.addLast(entry);
        }
      }
    }

    return bindingMap;
  }
3、parseBindView

解析 BindView

 private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
      Set<TypeElement> erasedTargetNames) {
      //获取 元素 的父元素 比如 Button 的 父元素是 Activity 
      //builderMap键值对 中的 key 原来是  element 的外层父元素 对应着 最外面的类 比如 Activity
    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

    // Start by verifying common generated code restrictions.
    boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
        || isBindingInWrongPackage(BindView.class, element);

    // Verify that the target type extends from View.
    //确认 目标 类型是基础继承 View 因为element是没有类型信息的 所以要转成 TypeMirror
    TypeMirror elementType = element.asType();
    //如果是泛型 那么就找到它的最高级上限类
    if (elementType.getKind() == TypeKind.TYPEVAR) {
      TypeVariable typeVariable = (TypeVariable) elementType;
      elementType = typeVariable.getUpperBound();
    }
    //限定名 名称
    Name qualifiedName = enclosingElement.getQualifiedName();
    Name simpleName = element.getSimpleName();
    if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {
      if (elementType.getKind() == TypeKind.ERROR) {
        note(element, "@%s field with unresolved type (%s) "
                + "must elsewhere be generated as a View or interface. (%s.%s)",
            BindView.class.getSimpleName(), elementType, qualifiedName, simpleName);
      } else {
        error(element, "@%s fields must extend from View or be an interface. (%s.%s)",
            BindView.class.getSimpleName(), qualifiedName, simpleName);
        hasError = true;
      }
    }

    if (hasError) {
      return;
    }

    // Assemble information on the field.
    //View  的 R.id.xxx
    int id = element.getAnnotation(BindView.class).value();
    //根据 enclosingElement 获取 对应的  BindingSet.Builder
    BindingSet.Builder builder = builderMap.get(enclosingElement);
    //转跳 3.1
    Id resourceId = elementToId(element, BindView.class, id);
    //如果已经拥有这个 builder 直接返回
    if (builder != null) {
      String existingBindingName = builder.findExistingBindingName(resourceId);
      if (existingBindingName != null) {
        error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
            BindView.class.getSimpleName(), id, existingBindingName,
            enclosingElement.getQualifiedName(), element.getSimpleName());
        return;
      }
    } else {
    //如果没有 转跳 4
      builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
    }

    String name = simpleName.toString();
    TypeName type = TypeName.get(elementType);
    boolean required = isFieldRequired(element);
    builder.addField(resourceId, new FieldViewBinding(name, type, required));

    // Add the type-erased version to the valid binding targets set.
    erasedTargetNames.add(enclosingElement);
  }

3.1

JCTree 抽象语法树 (AST)<br />用来描述程序代码语法结构的树形表示方式。语法树的每一个节点都代表着程序代码中的语法结构,例如包,类型,修饰符,接口,返回值,甚至代码注释等都是一个语法结构。

Scanner :语法分析类 <br />语法分析过程有这个类实现。

如果分析资源 id 没有该 id ,那么新生成一个 id

 private Id elementToId(Element element, Class<? extends Annotation> annotation, int value) {
    JCTree tree = (JCTree) trees.getTree(element, getMirror(element, annotation));
    if (tree != null) { // tree can be null if the references are compiled types and not source
      rScanner.reset();
      tree.accept(rScanner);
      if (!rScanner.resourceIds.isEmpty()) {
        return rScanner.resourceIds.values().iterator().next();
      }
    }
    return new Id(value);
  }
4、BindingSet#Builder
private BindingSet.Builder getOrCreateBindingBuilder(
      Map<TypeElement, BindingSet.Builder> builderMap, TypeElement enclosingElement) {
    // 先判断 enclosingElement 对应的 BindingSet.Builder 是否已存在 
    // 没有就创建一个
    BindingSet.Builder builder = builderMap.get(enclosingElement);
    if (builder == null) {
      // 创建 BindingSet.Builder 如何创建的 往下看
      builder = BindingSet.newBuilder(enclosingElement);
      // 添加到 builderMap
      builderMap.put(enclosingElement, builder);
    }
    return builder;
  }
static Builder newBuilder(TypeElement enclosingElement) {
    TypeMirror typeMirror = enclosingElement.asType();
    // 判断当前父元素的类型  是View Activity 还是 Dialog
    boolean isView = isSubtypeOfType(typeMirror, VIEW_TYPE);
    boolean isActivity = isSubtypeOfType(typeMirror, ACTIVITY_TYPE);
    boolean isDialog = isSubtypeOfType(typeMirror, DIALOG_TYPE);

    TypeName targetType = TypeName.get(typeMirror);
    if (targetType instanceof ParameterizedTypeName) {
      targetType = ((ParameterizedTypeName) targetType).rawType;
    }
    // 获取父类元素的包名
    String packageName = getPackage(enclosingElement).getQualifiedName().toString();
    // 获取父类元素的名称
    String className = enclosingElement.getQualifiedName().toString().substring(
        packageName.length() + 1).replace('.', '$');
    // 最终要生成的java类的名称 比如我们的 MainAcitity_ViewBinding 
    ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");
    // 判断父类元素是否为final类型
    boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL);
    return new Builder(targetType, bindingClassName, isFinal, isView, isActivity, isDialog);
  }

最后我们来看 BindingSet#brewJava

 JavaFile brewJava(int sdk, boolean debuggable) {
 //重点是 生成TypeSpec
    TypeSpec bindingConfiguration = createType(sdk, debuggable);
    return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
        .addFileComment("Generated code from Butter Knife. Do not modify!")
        .build();
  }
//根据 几个参数 来确定 整成代码的模板
  private TypeSpec createType(int sdk, boolean debuggable) {
    TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
        .addModifiers(PUBLIC);
    if (isFinal) {
      result.addModifiers(FINAL);
    }

    if (parentBinding != null) {
      result.superclass(parentBinding.bindingClassName);
    } else {
      result.addSuperinterface(UNBINDER);
    }

    if (hasTargetField()) {
      result.addField(targetTypeName, "target", PRIVATE);
    }

    if (isView) {
      result.addMethod(createBindingConstructorForView());
    } else if (isActivity) {
    //我们重点看这个
      result.addMethod(createBindingConstructorForActivity());
    } else if (isDialog) {
      result.addMethod(createBindingConstructorForDialog());
    }
    if (!constructorNeedsView()) {
      // Add a delegating constructor with a target type + view signature for reflective use.
      result.addMethod(createBindingViewDelegateConstructor());
    }
    result.addMethod(createBindingConstructor(sdk, debuggable));
//创造  Unbind  方法
    if (hasViewBindings() || parentBinding == null) {
      result.addMethod(createBindingUnbindMethod(result));
    }

    return result.build();
  }
 private MethodSpec createBindingConstructorForActivity() {
   //构造方法
    MethodSpec.Builder builder = MethodSpec.constructorBuilder()
        .addAnnotation(UI_THREAD)
        .addModifiers(PUBLIC)
        .addParameter(targetTypeName, "target");
   //构造方法中 生成下面代码 这是模板化
    if (constructorNeedsView()) {
      builder.addStatement("this(target, target.getWindow().getDecorView())");
    } else {
   
      builder.addStatement("this(target, target)");
    }
    return builder.build();
  }

  private MethodSpec createBindingConstructor(int sdk, boolean debuggable) {
    MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
       //添加 注解 @UiThread
       .addAnnotation(UI_THREAD)
       // public 
        .addModifiers(PUBLIC);
//实例:public MainActivity_ViewBinding(MainActivity target, View source) 
//如果view 绑定了 方法  设置 final 给 MainActivity target
    if (hasMethodBindings()) {
      constructor.addParameter(targetTypeName, "target", FINAL);
    } else {
      constructor.addParameter(targetTypeName, "target");
    }
//如果 需要视图 就是 View source
    if (constructorNeedsView()) {
      constructor.addParameter(VIEW, "source");
    } else {
    //否则就是 Context context
      constructor.addParameter(CONTEXT, "context");
    }

    if (hasUnqualifiedResourceBindings()) {
      // Aapt can change IDs out from underneath us, just suppress since all will work at runtime.
      constructor.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class)
          .addMember("value", "$S", "ResourceType")
          .build());
    }

    if (hasOnTouchMethodBindings()) {
      constructor.addAnnotation(AnnotationSpec.builder(SUPPRESS_LINT)
          .addMember("value", "$S", "ClickableViewAccessibility")
          .build());
    }

    if (parentBinding != null) {
      if (parentBinding.constructorNeedsView()) {
        constructor.addStatement("super(target, source)");
      } else if (constructorNeedsView()) {
        constructor.addStatement("super(target, source.getContext())");
      } else {
        constructor.addStatement("super(target, context)");
      }
      constructor.addCode("\n");
    }
    //  this.target = target;
    if (hasTargetField()) {
      constructor.addStatement("this.target = target");
      constructor.addCode("\n");
    }
//View 绑定 重点来了
    if (hasViewBindings()) {
      if (hasViewLocal()) {
        // Local variable in which all views will be temporarily stored.
        constructor.addStatement("$T view", VIEW);
      }
      for (ViewBinding binding : viewBindings) {
      //View 绑定语句
        addViewBinding(constructor, binding, debuggable);
      }
      for (FieldCollectionViewBinding binding : collectionBindings) {
        constructor.addStatement("$L", binding.render(debuggable));
      }

      if (!resourceBindings.isEmpty()) {
        constructor.addCode("\n");
      }
    }

    if (!resourceBindings.isEmpty()) {
      if (constructorNeedsView()) {
        constructor.addStatement("$T context = source.getContext()", CONTEXT);
      }
      if (hasResourceBindingsNeedingResource(sdk)) {
        constructor.addStatement("$T res = context.getResources()", RESOURCES);
      }
      for (ResourceBinding binding : resourceBindings) {
        constructor.addStatement("$L", binding.render(sdk));
      }
    }

    return constructor.build();
  }
  private void addViewBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
    if (binding.isSingleFieldBinding()) {
      // Optimize the common case where there's a single binding directly to a field.
      FieldViewBinding fieldBinding = requireNonNull(binding.getFieldBinding());
      CodeBlock.Builder builder = CodeBlock.builder()
          .add("target.$L = ", fieldBinding.getName());

      boolean requiresCast = requiresCast(fieldBinding.getType());
      if (!debuggable || (!requiresCast && !fieldBinding.isRequired())) {
        if (requiresCast) {
          builder.add("($T) ", fieldBinding.getType());
        }
        builder.add("source.findViewById($L)", binding.getId().code);
      } else {
        builder.add("$T.find", UTILS);
        builder.add(fieldBinding.isRequired() ? "RequiredView" : "OptionalView");
        if (requiresCast) {
          builder.add("AsType");
        }
        builder.add("(source, $L", binding.getId().code);
        if (fieldBinding.isRequired() || requiresCast) {
          builder.add(", $S", asHumanDescription(singletonList(fieldBinding)));
        }
        if (requiresCast) {
          builder.add(", $T.class", fieldBinding.getRawType());
        }
        builder.add(")");
      }
      result.addStatement("$L", builder.build());
      return;
    }

    List<MemberViewBinding> requiredBindings = binding.getRequiredBindings();
    if (!debuggable || requiredBindings.isEmpty()) {
      result.addStatement("view = source.findViewById($L)", binding.getId().code);
    } else if (!binding.isBoundToRoot()) {
    //添加 类似 Utils.findRequiredView(source, R.id.button, "field 'button' and method 'showToast'");
      result.addStatement("view = $T.findRequiredView(source, $L, $S)", UTILS,
          binding.getId().code, asHumanDescription(requiredBindings));
    }

    addFieldBinding(result, binding, debuggable);
    addMethodBindings(result, binding, debuggable);
  }
 private void addFieldBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
    FieldViewBinding fieldBinding = binding.getFieldBinding();
    if (fieldBinding != null) {
      if (requiresCast(fieldBinding.getType())) {
      //  类似  target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
        if (debuggable) {
          result.addStatement("target.$L = $T.castView(view, $L, $S, $T.class)",
              fieldBinding.getName(), UTILS, binding.getId().code,
              asHumanDescription(singletonList(fieldBinding)), fieldBinding.getRawType());
        } else {
          result.addStatement("target.$L = ($T) view", fieldBinding.getName(),
              fieldBinding.getType());
        }
      } else {
        result.addStatement("target.$L = view", fieldBinding.getName());
      }
    }
  }

总结:

一、利用 MainActivity_ViewBinding 类,插入 MainActivity 来对 View 进行绑定 和 设置 点击事件。<br />二、生成 MainActivity_ViewBinding。<br />1、遍历所有注解,找到我们自己的注解 以 BindView 为例子。找到所有 拥有 BindView 的元素 Element。<br />由于编译时期得不到 元素 Element 的 类信息,所以引入 Mirror 转换成 TypeElement 。<br />把所有遍历到的元素 转换成 TypeElement 然后 以 元素的父元素 比如 Activity 为 key 去存储 BindingSet.Builder。也就是 一个生成类 为一个 key。<br />2、BindingSet.Builder 负责按照 JavaPoet 的规则来创建 TypeSpec。 根据 元素 的类型 进行相应的 生成工作。<br />3、最后 javaFile.writeTo(filer); 生成我们需要的 MainActivity_ViewBinding 文件。

最后

「云开方见日,潮尽炉峰出。」

参考:<br />Java注解(3)-源码级框架<br />https://blog.csdn.net/duo2005duo/article/details/50541281<br />《深入理解 Java 虚拟机》

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值