一 、 從調用端分析
爲了方便理解咱們從最簡單最經常使用的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
有了上面的這些基礎知識,就能夠介紹下面的主角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文件就生成了。