ButterKnife源碼分析- 涉及元註解和AbstractProcessor和javapoet

版權聲明:本文爲博主原創文章,未經博主容許不得轉載。 https://blog.csdn.net/dingshuhong_/article/details/84306604

一 、 從調用端分析

爲了方便理解咱們從最簡單最經常使用的findViewById入手,一般咱們只須要寫下這樣一份代碼就能夠替代繁瑣的findViewById方法。html

@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目錄下的對應的包裏能夠找到這樣一份代碼,由於代碼行數很少,我就所有粘貼出來。java

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。android

驗證下咱們的猜測。編程

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方法。api

@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) 獲得一個構造方法markdown

經過Class.newInstance()Constructor.newInstance()兩種反射方法建立對象的異同:oracle

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的構造方法。

2、 探索編譯過程,什麼能力讓ButterKnife讓偷偷把活給幹了

只是一個build 莫名其妙就產生MainActivity_ViewBinding就被創造出來, 接下來咱們來看看MainActivity_ViewBinding是怎麼樣被創造出來的。

一、 元註解 - 註釋註解的註解

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

整個過程作了兩件事,1、解析BindView註解存放在Map<TypeElement, BindingSet.Builder> builderMap中 二、將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文件就生成了。

相關文章
相關標籤/搜索