一 、 从调用端分析
为了方便理解我们从最简单最常用的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
有了上面的这些基础知识,就可以介绍下面的主角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文件就生成了。