![1870221-df2120ae95c23303.png](https://i-blog.csdnimg.cn/blog_migrate/c669e8437d33bebc2b69d4da101436b0.webp?x-image-process=image/format,png)
「ButterKnife」是 Android 系统的 View 注入框架,极大的便利了我们的开发,对应用的性能基本没有影响,是我们开发中一把尖锐的匕首。<br /><br /><br />-- Mr.S
分析源码之前
ButterKnife 大神的 github ,最好的老师。
一、使用
使用的版本是 8.8.1 并不是最新的,因为最新的适配 androidx,会有一些问题,功能是一样的。
1、配置
dependencies {
......
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
//这一句
classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'
}
}
如果在 library 中 ,记得添加下面的代码。
apply plugin: 'com.jakewharton.butterknife'
2、简单使用
更多具体使用,参见其他博客。
public class MainActivity extends AppCompatActivity {
@BindView(R.id.button)
Button button;
@BindView(R.id.button2)
Button button2;
@BindView(R.id.textView)
TextView textView;
@OnClick(R.id.button)
public void showToast(Button button) {
Toast.makeText(this, "show_button", Toast.LENGTH_SHORT).show();
textView.setText("woahhahaha");
button.setText("woahhahaha");
}
@OnClick(R.id.textView)
public void showToast() {
Toast.makeText(this, "show_text", Toast.LENGTH_SHORT).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
}
二、使用须知
1、三大绑定情景
- 在Activity中绑定ButterKnife
- 在Fragment中绑定ButterKnife
- 在Adapter中绑定ButterKnife
2、丰富的功能
- 绑定View
- 绑定资源
- 事件绑定
- 绑定监听
- 使用findById
- 设置多个view的属性
- 更多事件注解
分析源码
一、看的见的源码
1、bind
我们发现肉眼可见的只有一个方法,一个框架能封装的如此简单,真是神一般的存在了。
ButterKnife.bind(this);
可以看出只在 UI 线程中执行。创建 目标对象 和 视图 的绑定。sourceView 就是我们的 DecorView。可以把这个Activity 可见得所有 view 都遍历到。
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
我们用 activity 代替目标对象,这样往下分析的时候大家可以清晰一点。
我们发现通过获得 activity 的 class 对象,然后通过 findBindingConstructorForClass 反射获得 Unbinder 的子类的构造方法,最后通过 constructor.newInstance(target, source) 反射获得 Unbinder 子类的对象。自此绑定结束。那么重点还是 findBindingConstructorForClass 方法。
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
//首先获得目标对象的 class 对象 ps:看到这个就知道要用反射了
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
//我们用 activity 代替目标对象,这样往下分析的时候大家可以清晰一点
//
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
//BINDINGS Map 先从 BINDINGS 中根据 class 对象 查找相应的 Unbinder 子类对象 有的话直接返回
//static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
//获取 activity 的类名
String clsName = cls.getName();
//如果是 android 和 java 开头 那么直接返回 null 这些是 framework 类
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
//这里就是是 “com.ssy.MainActivity_ViewBinding” 我们通过类加载器加载这个类
//为何会有这个类呢?我们下面去分析 这里写 略过
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
//反射得到这个类的的构造方法 最后返回这个 bindingCtor 这个构造方法
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
//加入 map 缓存
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
我们来整理一下,我们得到找到一个 类似 MainActivity 类的 MainActivity_ViewBinding ,然后调用了这个类的构造方法,获得了这个类的实例。这样前不着村,后不着店的,很是懵逼啊。
2、xxx_ViewBinding
唯一的线索就是 MainActivity_ViewBinding 类了,那么我们就查找一下这个类。这是 ButterKnife 自动生成的,我们之后在讨论这个类是如何生成的。<br />MainActivity_ViewBinding 实现了 Unbinder。
只有一个解除绑定的方法。
public interface Unbinder {
@UiThread void unbind();
Unbinder EMPTY = new Unbinder() {
@Override public void unbind() { }
};
}
注意:button 和 textView 都有监听事件 button2 没有任何事件。然后我们看下面代码的描述。
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
//这个是 button 用于 解绑
private View view2131165218;
//这个是 textView 用于 解绑
private View view2131165321;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(final MainActivity target, View source) {
this.target = target;
View view;
// 实际通过 source.findViewById 获得这个 view
//后面 who 字符串 用于 这个 view 的描述 变量名 :button 关联的监听事件方执行的方法是 showToast
view = Utils.findRequiredView(source, R.id.button, "field 'button' and method 'showToast'");
//因为这个 view 是 View 所以强转成具体 类 比如 Button 然后 赋值给 target.button
//这个其实就是引用 和 具体 view 的绑定
target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
//把 这个 局部变量view 赋值给 全局变量 view2131165218 用于 解绑的操作
view2131165218 = view;
//然年后 设置监听事件 调用 target 的 showToast 方法,参数是这个 点击事件的 view
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.showToast(Utils.castParam(p0, "doClick", 0, "showToast", 0, Button.class));
}
});
//同理 获得 button2 但是这个么有 onClick 事件 所以就省略了 上面的代码
target.button2 = Utils.findRequiredViewAsType(source, R.id.button2, "field 'button2'", Button.class);
//这个也是同理 button
view = Utils.findRequiredView(source, R.id.textView, "field 'textView' and method 'showToast'");
target.textView = Utils.castView(view, R.id.textView, "field 'textView'", TextView.class);
view2131165321 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
//区别是 这个 showToast 是无参数的
target.showToast();
}
});
}
//最后是解绑 上面的 view
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.button = null;
target.button2 = null;
target.textView = null;
view2131165218.setOnClickListener(null);
view2131165218 = null;
view2131165321.setOnClickListener(null);
view2131165321 = null;
}
}
我们通过这个生成类,发现它在 bind 方法内替我们做了我们嫌麻烦的事情,代码也很简单,如果我们用静态类的方法去实现的话,很简单,但是 ButterKnife 是动态生成。注意是编译器生成的,所以不影响应用的速度,如果是运行时的解释执行肯定会影响应用速度的。这就是运行时注解和编译时注解的区别了。
那么这个究竟是怎么生成的这个类呢?那就涉及到注解处理器了。
二、语言模型包的使用
1、Mirror<br />注解处理器因为操作的是源码,所以需要用到JAVA语言模型包,javax.lang.model及其子包都是Java的语言模型包。这个包是采用了Mirror设计,Java是一种可以自描述的语言,其反射机制就是一种自描述,传统的反射机制将自描述与其他操作合并在一起,Mirror机制将自描述跟其他操作隔离,自描述部分是Meta level,其他部分是Base level
[图片上传失败...(image-1b24e6-1549882808300)]
2、Element:<br />代表语言元素,比如包,类,方法等,但是Element并没有包含自身的信息,自身信息要通过Mirror来获取,每个Element都指向一个TypeMirror,这个TypeMirror里有自身的信息。通过下面获方法取Element中的Mirror
TypeMirror mirror=element.asType()
3、TypeMirror<br />TypeMirror类型是DeclaredType或者TypeVariable时候可以转化成Element
Types types=processingEnvironment.getTypeUtils()
Element element=types.asElement(typeMirror)
4、获取类型
获取Element或者TypeMirror的类型都是通过getKind()获取类型,但是返回值虽然不同,但是都是枚举。<br /><br />
ElementKind element.getKind()
TypeKind typeMirror.getKind()
对具体类型,要避免用instanceof,因为即使同一个类getKind()也有不同的结果。比如TypeElemnt的getKind()返回结果可以是枚举,类,注解,接口四种。<br /><br /><br />5、源码中移动
比如一个类里面有方法,成员变量等,这个类相对于方法跟成员变量就是外层元素,而成员变量和方法相对于类就是内层元素。 <br />Element是代表源码的类,源码中移动到其他位置必须是用Element,比如移动到当前元素的外层元素TypeElement。<br /><br />
public static TypeElement findEnclosingTypeElement( Element e ){
while( e != null && !(e instanceof TypeElement) ) {
e = e.getEnclosingElement();//通过getEnclosingElement()获取外层元素
}
return TypeElement.class.cast( e );
}
也可以使用getEnclosedElements()获取当前Element内层的所有元素。<br /><br /><br />6、类继承树结构中移动
TypeMirror是用来反应类本身信息的类,在继承树移动必须用到TypeMirror,比如查找某个类的父类
Types typeUtils = processingEnv.getTypeUtils();
while (!typeElement.toString().equals(Object.class.getName())) {
TypeMirror typeMirror=element.getSuperclass();
element = (TypeElement)typeUtils.asElement(typeMirror);
}
7、处理MirroredTypeException
编译器使用注解处理器来处理目标代码时候,目标源码还没有编译成字节码,所以任何指向目标源码字节码的代码都会发生MirroredTypeException。最常见的例子见下面
//已经编译的三方jar:
@Retention(RetentionPolicy.SOURCE)
@interface What{
Class<?> value()
}
//源码:
@What(A.class)
public class A{}
//注解处理器
public ProcessorA extends AbstractProcessor{
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
...
What what=typeElementA.getAnnotation(What.class);
Class<?> clazz=what.value(); //这里将有MirroredTypeException
...
}
}
注解处理器之所有MirroredTypeException,是因为此时类A还没有被编译成字节码,所以A.class不存在,解决这个问题需要异常,利用异常我能获取类A的名字等信息,但是却得不到A.class,如下
public ProcessorA extends AbstractProcessor{
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
...
What what=typeElementA.getAnnotation(What.class);
try {
Class<?> clazz=what.value(); //这里将有MirroredTypeException
}catch(MirroredTypeException mte){
TypeMirror classTypeMirror = mte.getTypeMirror();
TypeElement classTypeElement = (TypeElement)Types.asElement(classTypeMirro);
//获取canonicalName
String canonicalName= classTypeElement.getQualifiedName().toString();
//获取simple name
String simpleName = classTypeElement.getSimpleName().toString();
}
...
}
}
三、看不见的「注解处理器」
关于 注解处理器 可以看这篇文章
所以我们只要根据 APT 和 JavaPoet 的规则来生成 「MainActivity_ViewBinding」 就好了。
那么源码里是如何做的呢?我们抽取一部分来看一下。
我们要想生成 注入的 Java 代码 必须在这里处理
1、process
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//查询 和 解析所有的目标 目标是什么呢?
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
//遍历 执行 binding 的 brewJava 又是什么意思呢? 跳转 4
JavaFile javaFile = binding.brewJava(sdk, debuggable);
try {
//最后 写入 Java 文件中 这个我们是清楚的
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
带着疑问 继续往下看。
2、findAndParseTargets
查找并解析所有的注解目标
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
//首先 是 Map 这个很重要 以 类型元素 为 key 保存 BindingSet.Builder 具体我们再接着往下看
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
//被擦除的目标名称 集合
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
···
//我们只需要看绑定 BindView 就好了 其他的都差不多 获得
//getElementsAnnotatedWith 查找到 所有使用 BindView 注解 的元素
// Process each @BindView element.
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
// we don't SuperficialValidation.validateElement(element)
// so that an unresolved View type can be generated by later processing rounds
try {
//解析 我们 BindView 元素 我们看看 是怎么 处理 builderMap 和 erasedTargetNames
//跳转 3
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
···
// Associate superclass binders with their subclass binders. This is a queue-based tree walk
// which starts at the roots (superclasses) and walks to the leafs (subclasses).
//将 builderMap 中的数据 添加到 队列中
Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
new ArrayDeque<>(builderMap.entrySet());
Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
//把 擦除的目标 从 Set 里剔除
while (!entries.isEmpty()) {
//出列
Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();
TypeElement type = entry.getKey();
BindingSet.Builder builder = entry.getValue();
//是否 是 擦除的目标名字
TypeElement parentType = findParentType(type, erasedTargetNames);
//如果没有父类 那么直接保存 type 和对应的额BindSet
if (parentType == null) {
bindingMap.put(type, builder.build());
} else {
//如果有父元素 那么从 bindingMap 查看是否有
BindingSet parentBinding = bindingMap.get(parentType);
//如果有 bindingMap 则给它设置 父 bindingMap
if (parentBinding != null) {
builder.setParent(parentBinding);
bindingMap.put(type, builder.build());
} else {
//如果没有 再次入队列
// Has a superclass binding but we haven't built it yet. Re-enqueue for later.
entries.addLast(entry);
}
}
}
return bindingMap;
}
3、parseBindView
解析 BindView
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames) {
//获取 元素 的父元素 比如 Button 的 父元素是 Activity
//builderMap键值对 中的 key 原来是 element 的外层父元素 对应着 最外面的类 比如 Activity
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// Start by verifying common generated code restrictions.
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
|| isBindingInWrongPackage(BindView.class, element);
// Verify that the target type extends from View.
//确认 目标 类型是基础继承 View 因为element是没有类型信息的 所以要转成 TypeMirror
TypeMirror elementType = element.asType();
//如果是泛型 那么就找到它的最高级上限类
if (elementType.getKind() == TypeKind.TYPEVAR) {
TypeVariable typeVariable = (TypeVariable) elementType;
elementType = typeVariable.getUpperBound();
}
//限定名 名称
Name qualifiedName = enclosingElement.getQualifiedName();
Name simpleName = element.getSimpleName();
if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {
if (elementType.getKind() == TypeKind.ERROR) {
note(element, "@%s field with unresolved type (%s) "
+ "must elsewhere be generated as a View or interface. (%s.%s)",
BindView.class.getSimpleName(), elementType, qualifiedName, simpleName);
} else {
error(element, "@%s fields must extend from View or be an interface. (%s.%s)",
BindView.class.getSimpleName(), qualifiedName, simpleName);
hasError = true;
}
}
if (hasError) {
return;
}
// Assemble information on the field.
//View 的 R.id.xxx
int id = element.getAnnotation(BindView.class).value();
//根据 enclosingElement 获取 对应的 BindingSet.Builder
BindingSet.Builder builder = builderMap.get(enclosingElement);
//转跳 3.1
Id resourceId = elementToId(element, BindView.class, id);
//如果已经拥有这个 builder 直接返回
if (builder != null) {
String existingBindingName = builder.findExistingBindingName(resourceId);
if (existingBindingName != null) {
error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
BindView.class.getSimpleName(), id, existingBindingName,
enclosingElement.getQualifiedName(), element.getSimpleName());
return;
}
} else {
//如果没有 转跳 4
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
}
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
boolean required = isFieldRequired(element);
builder.addField(resourceId, new FieldViewBinding(name, type, required));
// Add the type-erased version to the valid binding targets set.
erasedTargetNames.add(enclosingElement);
}
3.1
JCTree 抽象语法树 (AST)<br />用来描述程序代码语法结构的树形表示方式。语法树的每一个节点都代表着程序代码中的语法结构,例如包,类型,修饰符,接口,返回值,甚至代码注释等都是一个语法结构。
Scanner :语法分析类 <br />语法分析过程有这个类实现。
如果分析资源 id 没有该 id ,那么新生成一个 id
private Id elementToId(Element element, Class<? extends Annotation> annotation, int value) {
JCTree tree = (JCTree) trees.getTree(element, getMirror(element, annotation));
if (tree != null) { // tree can be null if the references are compiled types and not source
rScanner.reset();
tree.accept(rScanner);
if (!rScanner.resourceIds.isEmpty()) {
return rScanner.resourceIds.values().iterator().next();
}
}
return new Id(value);
}
4、BindingSet#Builder
private BindingSet.Builder getOrCreateBindingBuilder(
Map<TypeElement, BindingSet.Builder> builderMap, TypeElement enclosingElement) {
// 先判断 enclosingElement 对应的 BindingSet.Builder 是否已存在
// 没有就创建一个
BindingSet.Builder builder = builderMap.get(enclosingElement);
if (builder == null) {
// 创建 BindingSet.Builder 如何创建的 往下看
builder = BindingSet.newBuilder(enclosingElement);
// 添加到 builderMap
builderMap.put(enclosingElement, builder);
}
return builder;
}
static Builder newBuilder(TypeElement enclosingElement) {
TypeMirror typeMirror = enclosingElement.asType();
// 判断当前父元素的类型 是View Activity 还是 Dialog
boolean isView = isSubtypeOfType(typeMirror, VIEW_TYPE);
boolean isActivity = isSubtypeOfType(typeMirror, ACTIVITY_TYPE);
boolean isDialog = isSubtypeOfType(typeMirror, DIALOG_TYPE);
TypeName targetType = TypeName.get(typeMirror);
if (targetType instanceof ParameterizedTypeName) {
targetType = ((ParameterizedTypeName) targetType).rawType;
}
// 获取父类元素的包名
String packageName = getPackage(enclosingElement).getQualifiedName().toString();
// 获取父类元素的名称
String className = enclosingElement.getQualifiedName().toString().substring(
packageName.length() + 1).replace('.', '$');
// 最终要生成的java类的名称 比如我们的 MainAcitity_ViewBinding
ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");
// 判断父类元素是否为final类型
boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL);
return new Builder(targetType, bindingClassName, isFinal, isView, isActivity, isDialog);
}
最后我们来看 BindingSet#brewJava
JavaFile brewJava(int sdk, boolean debuggable) {
//重点是 生成TypeSpec
TypeSpec bindingConfiguration = createType(sdk, debuggable);
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
//根据 几个参数 来确定 整成代码的模板
private TypeSpec createType(int sdk, boolean debuggable) {
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
.addModifiers(PUBLIC);
if (isFinal) {
result.addModifiers(FINAL);
}
if (parentBinding != null) {
result.superclass(parentBinding.bindingClassName);
} else {
result.addSuperinterface(UNBINDER);
}
if (hasTargetField()) {
result.addField(targetTypeName, "target", PRIVATE);
}
if (isView) {
result.addMethod(createBindingConstructorForView());
} else if (isActivity) {
//我们重点看这个
result.addMethod(createBindingConstructorForActivity());
} else if (isDialog) {
result.addMethod(createBindingConstructorForDialog());
}
if (!constructorNeedsView()) {
// Add a delegating constructor with a target type + view signature for reflective use.
result.addMethod(createBindingViewDelegateConstructor());
}
result.addMethod(createBindingConstructor(sdk, debuggable));
//创造 Unbind 方法
if (hasViewBindings() || parentBinding == null) {
result.addMethod(createBindingUnbindMethod(result));
}
return result.build();
}
private MethodSpec createBindingConstructorForActivity() {
//构造方法
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
.addAnnotation(UI_THREAD)
.addModifiers(PUBLIC)
.addParameter(targetTypeName, "target");
//构造方法中 生成下面代码 这是模板化
if (constructorNeedsView()) {
builder.addStatement("this(target, target.getWindow().getDecorView())");
} else {
builder.addStatement("this(target, target)");
}
return builder.build();
}
private MethodSpec createBindingConstructor(int sdk, boolean debuggable) {
MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
//添加 注解 @UiThread
.addAnnotation(UI_THREAD)
// public
.addModifiers(PUBLIC);
//实例:public MainActivity_ViewBinding(MainActivity target, View source)
//如果view 绑定了 方法 设置 final 给 MainActivity target
if (hasMethodBindings()) {
constructor.addParameter(targetTypeName, "target", FINAL);
} else {
constructor.addParameter(targetTypeName, "target");
}
//如果 需要视图 就是 View source
if (constructorNeedsView()) {
constructor.addParameter(VIEW, "source");
} else {
//否则就是 Context context
constructor.addParameter(CONTEXT, "context");
}
if (hasUnqualifiedResourceBindings()) {
// Aapt can change IDs out from underneath us, just suppress since all will work at runtime.
constructor.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType")
.build());
}
if (hasOnTouchMethodBindings()) {
constructor.addAnnotation(AnnotationSpec.builder(SUPPRESS_LINT)
.addMember("value", "$S", "ClickableViewAccessibility")
.build());
}
if (parentBinding != null) {
if (parentBinding.constructorNeedsView()) {
constructor.addStatement("super(target, source)");
} else if (constructorNeedsView()) {
constructor.addStatement("super(target, source.getContext())");
} else {
constructor.addStatement("super(target, context)");
}
constructor.addCode("\n");
}
// this.target = target;
if (hasTargetField()) {
constructor.addStatement("this.target = target");
constructor.addCode("\n");
}
//View 绑定 重点来了
if (hasViewBindings()) {
if (hasViewLocal()) {
// Local variable in which all views will be temporarily stored.
constructor.addStatement("$T view", VIEW);
}
for (ViewBinding binding : viewBindings) {
//View 绑定语句
addViewBinding(constructor, binding, debuggable);
}
for (FieldCollectionViewBinding binding : collectionBindings) {
constructor.addStatement("$L", binding.render(debuggable));
}
if (!resourceBindings.isEmpty()) {
constructor.addCode("\n");
}
}
if (!resourceBindings.isEmpty()) {
if (constructorNeedsView()) {
constructor.addStatement("$T context = source.getContext()", CONTEXT);
}
if (hasResourceBindingsNeedingResource(sdk)) {
constructor.addStatement("$T res = context.getResources()", RESOURCES);
}
for (ResourceBinding binding : resourceBindings) {
constructor.addStatement("$L", binding.render(sdk));
}
}
return constructor.build();
}
private void addViewBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
if (binding.isSingleFieldBinding()) {
// Optimize the common case where there's a single binding directly to a field.
FieldViewBinding fieldBinding = requireNonNull(binding.getFieldBinding());
CodeBlock.Builder builder = CodeBlock.builder()
.add("target.$L = ", fieldBinding.getName());
boolean requiresCast = requiresCast(fieldBinding.getType());
if (!debuggable || (!requiresCast && !fieldBinding.isRequired())) {
if (requiresCast) {
builder.add("($T) ", fieldBinding.getType());
}
builder.add("source.findViewById($L)", binding.getId().code);
} else {
builder.add("$T.find", UTILS);
builder.add(fieldBinding.isRequired() ? "RequiredView" : "OptionalView");
if (requiresCast) {
builder.add("AsType");
}
builder.add("(source, $L", binding.getId().code);
if (fieldBinding.isRequired() || requiresCast) {
builder.add(", $S", asHumanDescription(singletonList(fieldBinding)));
}
if (requiresCast) {
builder.add(", $T.class", fieldBinding.getRawType());
}
builder.add(")");
}
result.addStatement("$L", builder.build());
return;
}
List<MemberViewBinding> requiredBindings = binding.getRequiredBindings();
if (!debuggable || requiredBindings.isEmpty()) {
result.addStatement("view = source.findViewById($L)", binding.getId().code);
} else if (!binding.isBoundToRoot()) {
//添加 类似 Utils.findRequiredView(source, R.id.button, "field 'button' and method 'showToast'");
result.addStatement("view = $T.findRequiredView(source, $L, $S)", UTILS,
binding.getId().code, asHumanDescription(requiredBindings));
}
addFieldBinding(result, binding, debuggable);
addMethodBindings(result, binding, debuggable);
}
private void addFieldBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
FieldViewBinding fieldBinding = binding.getFieldBinding();
if (fieldBinding != null) {
if (requiresCast(fieldBinding.getType())) {
// 类似 target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
if (debuggable) {
result.addStatement("target.$L = $T.castView(view, $L, $S, $T.class)",
fieldBinding.getName(), UTILS, binding.getId().code,
asHumanDescription(singletonList(fieldBinding)), fieldBinding.getRawType());
} else {
result.addStatement("target.$L = ($T) view", fieldBinding.getName(),
fieldBinding.getType());
}
} else {
result.addStatement("target.$L = view", fieldBinding.getName());
}
}
}
总结:
一、利用 MainActivity_ViewBinding 类,插入 MainActivity 来对 View 进行绑定 和 设置 点击事件。<br />二、生成 MainActivity_ViewBinding。<br />1、遍历所有注解,找到我们自己的注解 以 BindView 为例子。找到所有 拥有 BindView 的元素 Element。<br />由于编译时期得不到 元素 Element 的 类信息,所以引入 Mirror 转换成 TypeElement 。<br />把所有遍历到的元素 转换成 TypeElement 然后 以 元素的父元素 比如 Activity 为 key 去存储 BindingSet.Builder。也就是 一个生成类 为一个 key。<br />2、BindingSet.Builder 负责按照 JavaPoet 的规则来创建 TypeSpec。 根据 元素 的类型 进行相应的 生成工作。<br />3、最后 javaFile.writeTo(filer); 生成我们需要的 MainActivity_ViewBinding 文件。
最后
「云开方见日,潮尽炉峰出。」
参考:<br />Java注解(3)-源码级框架<br />https://blog.csdn.net/duo2005duo/article/details/50541281<br />《深入理解 Java 虚拟机》