ButterKnife源码分析- 涉及元注解和AbstractProcessor和javapoet

一 、 从调用端分析

为了方便理解我们从最简单最常用的findViewById入手,通常我们只需要写下这样一份代码就可以替代繁琐的findViewById方法。

@BindView(R.id.toolbar)
Toolbar toolbar;

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

它究竟干了什么,编写完成上面的代码后,build一下,在\build\generated\source\apt\debug目录下的对应的包里可以找到这样一份代码,因为代码行数不多,我就全部粘贴出来。

import android.support.annotation.CallSuper;
import android.support.annotation.UiThread;
import android.view.View;
import android.widget.Toolbar;
import butterknife.Unbinder;
import butterknife.internal.Utils;
import java.lang.IllegalStateException;
import java.lang.Override;

public class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;

  @UiThread
  public MainActivity_ViewBinding(MainActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public MainActivity_ViewBinding(MainActivity target, View source) {
    this.target = target;

    target.toolbar = Utils.findRequiredViewAsType(source, R.id.toolbar, "field 'toolbar'", Toolbar.class);
  }

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

    target.toolbar = null;
  }
}

可以看到他会自动生成这样一个类, 其中target就是mainactivity本身target.toolbar = Utils.findRequiredViewAsType(source, R.id.toolbar, "field 'toolbar'", Toolbar.class);从这句可以猜想出他干了什么。就是我们要找的findViewById。

验证下我们的猜想。

public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
    Class<T> cls) {
  View view = findRequiredView(source, id, who); //核心在这里
  return castView(view, id, who, cls);  // 理解成类型转换
}

public static View findRequiredView(View source, @IdRes int id, String who) {
    View view = source.findViewById(id);
    if (view != null) {
      return view;
    }
    String name = getResourceEntryName(source, id);
    throw new IllegalStateException("Required view '"+ name + "' with ID "+ id+ " for "
        + who+ " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'" + " (methods) annotation.");
}

了解了上面这些, 接下来看看是怎么使用这个自动生成的类里的方法。 猜测就是ButterKnife.bind(this)这句造成调用了findViewById方法。

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

这里感觉并没有什么线索,继续往下看

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);
  } 
  //为了节约行数这里省掉一些代码
}

我们找到一句可能比较核心的代码findBindingConstructorForClass() 通过class 找到一个构造方法, 简单这样理解。最后执行constructor.newInstance(target, source) 得到一个构造方法

通过Class.newInstance()Constructor.newInstance()两种反射方法创建对象的异同:

Class.newInstance() 只可以反射无参构造

Constructor.newInstance()可以反射任何构造

看到这里,大家应该已经猜到了,我们的findViewById是怎样实现的。 ButterKnife.bind(this) 实际就是执行了MainActivity_ViewBinding的构造方法。 是不是很简单, 我们验证一下。

@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
  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<?> 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());
    bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
  } catch (NoSuchMethodException e) {
    throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
  }
  BINDINGS.put(cls, bindingCtor);
  return bindingCtor;
}

看到一大坨代码是不是很慌,核心代码很简单。

Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);

通过ClassLoader获取到MainActivity_ViewBinding.class 然后获取到构造Constructor 构造器。最终newInstance创建出来。

小结: 到这里我们就知道大名鼎鼎的ButterKnife是怎么运行的了。ButterKnife.bind(this)其实调用的是MainActivity_ViewBinding的构造方法。

二、 探索编译过程,什么能力让ButterKnife让偷偷把活给干了

只是一个build 莫名其妙就产生MainActivity_ViewBinding就被创造出来, 接下来我们来看看MainActivity_ViewBinding是怎么样被创造出来的。

1、 元注解 - 注释注解的注解

java中的四个元注解:@Retention,@Target,@Documented,@Inherited.

  • @Retention 注解的保留策略 (申明注解在代码中保留的生命周期)

    • SOURCE 注释只在源代码级别保留,编译时被忽略
    • CLASS 注释将被编译器在类文件中记录,但在运行时不需要JVM保留。这是默认的行为
    • RUNTIME 注释将被编译器记录在类文件中,在运行时保留JVM,因此可以反读。
  • @Target 申明注解作用的位置 (如 类 接口 方法 字段。。)

@Target(ElementType.TYPE)   //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR)  //构造函数
  • @Documented 注解表明这个注释是由 javadoc记录的
  • @Inherited 使用此注解,子类可以获取父类的注解信息,但是对方法和属性无效

看完了上面这些,我们先看一个范例

@Retention(RUNTIME) //运行时注解
@Target(FIELD) //作用于字段之上
public @interface BindView {
  /** View ID to which the field will be bound. */
  @IdRes int value();
}
@Target(METHOD) 
@Retention(RUNTIME)
@ListenerClass(// 自定义注解ListenerClass
    targetType = "android.view.View",
    setter = "setOnClickListener",
    type = "butterknife.internal.DebouncingOnClickListener",
    method = @ListenerMethod(//自定义注解 ListenerMethod
        name = "doClick",
        parameters = "android.view.View"
    )
)
public @interface OnClick {
  /** View IDs to which the method will be bound. */
  @IdRes int[] value() default { View.NO_ID };
}

它是我们要理解的核心,理解了它才能理解怎样创造MainActivity_ViewBinding

2 、Element与TypeMirror

**Element **子接口一共有以下几个ExecutableElement, PackageElement, Parameterizable, QualifiedNameable, TypeElement, TypeParameterElement, VariableElement

关于这个类的介绍

表示一个程序元素,比如包、类或者方法。每个元素都表示一个静态的语言级构造(不表示虚拟机的运行时构造)。

参考官方的文档 http://210.34.136.253:8488/javaprog/JDK1_6_api_html/javax/lang/model/element/Element.html

简单介绍几个常见的。

TypeElement 表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。注意,枚举类型是一种类,而注释类型是一种接口

ExecutableElement 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。

VariableElement 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。

TypeMirror

Java 编程语言中的类型。这些类型包括基本类型、声明类型(类和接口类型)、数组类型、类型变量和 null 类型。还可以表示通配符类型参数、executable 的签名和返回类型,以及对应于包和关键字 void 的伪类型。

已知的子接口有ArrayType, DeclaredType, ErrorType, ExecutableType, NoType, NullType, PrimitiveType, ReferenceType, TypeVariable, WildcardType

参考官方文档 https://download.oracle.com/technetwork/java/javase/6/docs/zh/api/javax/lang/model/type/TypeMirror.html

有了上面的这些基础知识,就可以介绍下面的主角AbstractProcessor

2 、 注解处理器-AbstractProcessor

核心方法介绍

一共有四个比较重要的方法

@AutoService(Processor.class)
public class WXAutoProcessor extends AbstractProcessor {

    /**
     * 它会被注解处理工具调用,做初始化处理,提供Elements,Types,Filer,Messager等工具类
     * 所有的实例都在ProcessingEnvironment
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }
    /**
     * 注册构建需要处理的注解集合
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotationNames = new LinkedHashSet<>();
        Set<Class<? extends Annotation>> annotations = getsupportAnnotations();
        for (Class<? extends Annotation> annotation : annotations) {
            annotationNames.add(annotation.getCanonicalName());
        }
        return annotationNames;
    }


    private Set<Class<? extends Annotation>> getsupportAnnotations() {
        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
        annotations.add(WXEntryGenerator.class);
        return annotations;
    }

    /**
     * 编码实现扫描,处理注解,生成 java 文件
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        generateEntry(roundEnvironment);
        return true;
    }

    /**
     * 用来指定你使用的 java 版本
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return super.getSupportedSourceVersion();
    }
}

接下来我们进入源码看看ButterKnife的实现\butterknife-compiler\src\main\java\butterknife\compiler\ButterKnifeProcessor.java

@Override public synchronized void init(ProcessingEnvironment env) {
  super.init(env);

  String sdk = env.getOptions().get(OPTION_SDK_INT);
  if (sdk != null) {
    try {
      this.sdk = Integer.parseInt(sdk);
    } catch (NumberFormatException e) {
      env.getMessager()
          .printMessage(Kind.WARNING, "Unable to parse supplied minSdk option '"
              + sdk
              + "'. Falling back to API 1 support.");
    }
  }

  debuggable = !"false".equals(env.getOptions().get(OPTION_DEBUGGABLE));
  useLegacyTypes = !hasAndroidX(env.getElementUtils());

  typeUtils = env.getTypeUtils();
  filer = env.getFiler();
  try {
    trees = Trees.instance(processingEnv);
  } catch (IllegalArgumentException ignored) {
  }
}

init

先看init方法, 看不懂具体做了什么,感觉并没啥用。

@Override public Set<String> getSupportedAnnotationTypes() {
  Set<String> types = new LinkedHashSet<>();
  for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
    types.add(annotation.getCanonicalName());
  }
  return types;
}

private Set<Class<? extends Annotation>> getSupportedAnnotations() {
  Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();

  annotations.add(BindAnim.class);
  annotations.add(BindArray.class);
  annotations.add(BindBitmap.class);
  annotations.add(BindBool.class);
  annotations.add(BindColor.class);
  annotations.add(BindDimen.class);
  annotations.add(BindDrawable.class);
  annotations.add(BindFloat.class);
  annotations.add(BindFont.class);
  annotations.add(BindInt.class);
  annotations.add(BindString.class);
  annotations.add(BindView.class);
  annotations.add(BindViews.class);
  annotations.addAll(LISTENERS);

  return annotations;
}

getSupportedAnnotations

接下来是getSupportedAnnotations 我们看到了它把所有定义的注解放入一个set集合并返回。并取出所有注解类的全路径保存在set集合中。

@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, useLegacyTypes);
    try {
      javaFile.writeTo(filer);
    } catch (IOException e) {
      error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
    }
  }

  return false;
}

核心的process 方法

接下来就是process 扫描 分析处理注解 生成对应的java文件。看看第一行做了些什么。

private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
  Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
  Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();

  // 解析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 {
      parseBindView(element, builderMap, erasedTargetNames);
    } catch (Exception e) {
      logParsingError(element, BindView.class, e);
    }
  }

   //把<Map.Entry<TypeElement, BindingSet.Builder>  转成一个 Map<TypeElement, BindingSet>
    
  // 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).
  Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
      new ArrayDeque<>(builderMap.entrySet());
  Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
  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);
    if (parentType == null) {
      bindingMap.put(type, builder.build());
    } else {
      BindingSet parentBinding = bindingMap.get(parentType);
      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;
}

整个过程做了两件事,一、解析BindView注解存放在Map<TypeElement, BindingSet.Builder> builderMap中 2、将builderMap 转成 bindingMap(Map<TypeElement, BindingSet> bindingMap)

第一步解析BindView注解
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
    Set<TypeElement> erasedTargetNames) {
  TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

  // 第一步 检查 isInaccessibleViaGeneratedCode 检查 MainActivity 的Modifier(修饰符)是否PRIVATE 和 STATIC,检查Kind 是否是Class ; isBindingInWrongPackage 检查enclosingElement是否是系统相关的包名
  // Start by verifying common generated code restrictions.
  boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
      || isBindingInWrongPackage(BindView.class, element);

  // 判断element是View的子类或者接口
  // Verify that the target type extends from View.
  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.
  int id = element.getAnnotation(BindView.class).value();
  BindingSet.Builder builder = builderMap.get(enclosingElement);
  Id resourceId = elementToId(element, BindView.class, id);
  if (builder != null) {
    String existingBindingName = builder.findExistingBindingName(resourceId);
    // 判断是否绑定过这个Id
    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 {
    //获取或者创建一个 BindingSet.Builder 
    builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
  }

  String name = simpleName.toString();
  TypeName type = TypeName.get(elementType);
  boolean required = isFieldRequired(element);

  // 将name type 和 required 封装到FieldViewBinding 中,最后添加到BindingSet.Builder中
  builder.addField(resourceId, new FieldViewBinding(name, type, required));

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

上面是完成的解析相关的代码,前面全部是检查的代码, 只有最后几行是最主要的builder.addField(resourceId, new FieldViewBinding(name, type, required))

将得到的BindView的注解信息都保存在builder。

看看builder是如何创建的。

private BindingSet.Builder getOrCreateBindingBuilder(
    Map<TypeElement, BindingSet.Builder> builderMap, TypeElement enclosingElement) {
  BindingSet.Builder builder = builderMap.get(enclosingElement);
  if (builder == null) {
    builder = BindingSet.newBuilder(enclosingElement);
    builderMap.put(enclosingElement, builder);
  }
  return builder;
}

通过一个 BindingSet.newBuilder(enclosingElement); 创建继续看看是如何创建的。BindingSet是什么东西

static Builder newBuilder(TypeElement enclosingElement) {
  TypeMirror typeMirror = enclosingElement.asType();

  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('.', '$');
      
      // 看看发现了什么MainActivity_ViewBinding  就是这样来的。
  ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");

  boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL);
  return new Builder(targetType, bindingClassName, isFinal, isView, isActivity, isDialog);
}

至此我们知道了MainActivity_ViewBinding这个名字是如何来的。 通知也发现了一个类BindingSet。

第二步将builderMap 转成 bindingMap(Map<TypeElement, BindingSet> bindingMap)
// 可变数组对象
Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
    new ArrayDeque<>(builderMap.entrySet());
Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
while (!entries.isEmpty()) {
 //取出数组中的第一个Map.Entry<TypeElement, BindingSet.Builder>
  Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();
  //
  TypeElement type = entry.getKey();
  BindingSet.Builder builder = entry.getValue();
  //判断是否有父类 如果有 就通过setParent设置进去
  TypeElement parentType = findParentType(type, erasedTargetNames);
  if (parentType == null) {
    bindingMap.put(type, builder.build());
  } else {
    BindingSet parentBinding = bindingMap.get(parentType);
    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);
    }
  }
}
第三步 生成文件TypeElement_ViewBinding 也就是MainActivity_ViewBinding
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
  TypeElement typeElement = entry.getKey();
  BindingSet binding = entry.getValue();

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

调用了BindingSet.brewJava 方法,看不出来具体干了啥。

JavaFile brewJava(int sdk, boolean debuggable, boolean useLegacyTypes) {
  TypeSpec bindingConfiguration = createType(sdk, debuggable, useLegacyTypes);
  return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
      .addFileComment("Generated code from Butter Knife. Do not modify!")
      .build();
}

同样是看不出来干了啥, 但是通过JavaFile.builder().build() 返回了一个JavaFile。 这中间调用了一个createType()方法。

javapoet

JavaFile是啥搞不懂? 查了一下,其实他是一个java自动生成代码的框架(javapoet是android之神JakeWharton开源的一款快速代码生成工具) 。可以看到生成代码就靠它了。 怎样生成代码呢?

MethodSpec main = MethodSpec.methodBuilder("main")
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
    .returns(void.class)
    .addParameter(String[].class, "args")
    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
	.addModifiers(Modifier.PUBLIC)
	.addMethod(main)
	.build();

JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld).build();

javaFile.writeTo(System.out);

接下来看createType 方法

private TypeSpec createType(int sdk, boolean debuggable, boolean useLegacyTypes) {
  	// 类的全路径 和 修饰符 public和 final
    TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
      .addModifiers(PUBLIC);
  if (isFinal) {
    result.addModifiers(FINAL);
  }
	// 判断是否有父类,如果有设置父类名字
  if (parentBinding != null) {
    result.superclass(parentBinding.bindingClassName);
  } else {
      //没有就是实现UNBINDER接口
    result.addSuperinterface(UNBINDER);
  }

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

  if (isView) {
    result.addMethod(createBindingConstructorForView(useLegacyTypes));
  } else if (isActivity) {
    result.addMethod(createBindingConstructorForActivity(useLegacyTypes));
  } else if (isDialog) {
    result.addMethod(createBindingConstructorForDialog(useLegacyTypes));
  }
  if (!constructorNeedsView()) {
    // Add a delegating constructor with a target type + view signature for reflective use.
    result.addMethod(createBindingViewDelegateConstructor(useLegacyTypes));
  }
    //创建构造函数
  result.addMethod(createBindingConstructor(sdk, debuggable, useLegacyTypes));

    //创建unbind方法
  if (hasViewBindings() || parentBinding == null) {
    result.addMethod(createBindingUnbindMethod(result, useLegacyTypes));
  }

  return result.build();
}

从上面那个生成HelloWorld的例子 可以很清楚的知道这个方法就是创建一个TypeSpec 。 有了它 就可以创建一个类。

private MethodSpec createBindingConstructor(int sdk, boolean debuggable, boolean useLegacyTypes) {
    //public 和 ui_thread修饰符
  MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
      .addAnnotation(useLegacyTypes ? UI_THREAD_LEGACY : UI_THREAD)
      .addModifiers(PUBLIC);
	// 判断注解是否是绑定在方法上,如果是的就增加一个final字段修饰
  if (hasMethodBindings()) {
    constructor.addParameter(targetTypeName, "target", FINAL);
  } else {
    constructor.addParameter(targetTypeName, "target");
  }

  if (constructorNeedsView()) {
    constructor.addParameter(VIEW, "source");
  } else {
    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());
  }
	//同hasMethodBindings
  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");
  }
  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) {
        // 构建findviewbyid方法
      addViewBinding(constructor, binding, debuggable, useLegacyTypes);
    }
    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();
}

看返回值类型就知道 这个是创建java类的另一个重要元素MethodSpec。看addViewBinding方法

private void addViewBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable,
    boolean useLegacyTypes) {
  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;
  }

在这里我就找到了我们想要的findViewById方法产生的位置。

最后

JavaFile javaFile = binding.brewJava(sdk, debuggable, useLegacyTypes);

javaFile.writeTo(filer);

我们的java文件就生成了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值