本文基于ButterKnife 8.8.1,主要分析@BindView注解的相关部分。
查看ButterKnife的源码,获知这是通过使用APT(Annotation Processing Tool)即注解处理器,在程序编译期扫描和处理注解Activity_ViewBinding的java文件。 (有关注解处理器,请查看这边文章,讲解比较详细)
ButterKnife的compiler包,编译注解处理器就在这里。
ButterKnifeProcessor .png
package butterknife.compiler;
public final class ButterKnifeProcessor extends AbstractProcessor {
}
ButterKnifeProcessor继承了AbstractProcessor,是生成Activity_ViewBinding的核心类,接下来分析该类和与其相关联的类(BindingSet,ViewBinding)的源码,主要分析最常用的控件@BindView注解和控件绑定。
1、init(ProcessingEnvironment env)
private Types typeUtils;//处理TypeMirror的工具类
private Filer filer;//用于ViewBinding创建文件
private @Nullable Trees trees;
private int sdk = 1;
private boolean debuggable = true;//判断是否开启debug模式
@Override public synchronized void init(ProcessingEnvironment env) {
super.init(env);
String sdk = env.getOptions().get(OPTION_SDK_INT);//获取app的sdk版本
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));
typeUtils = env.getTypeUtils();
filer = env.getFiler();
try {
trees = Trees.instance(processingEnv);
} catch (IllegalArgumentException ignored) {
}
}
注:init()方法主要执行获取typeUtils和filer工具,判断app当前版本,判断是否开启debug模式。
2、getSupportedAnnotationTypes()
//注册自定义的注解
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
//添加自定义的注解,BindView、BindViews等等
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;
}
注:getSupportedAnnotationTypes()定义注解处理器注册到哪些注解上
3、process(Set<? extends TypeElement> annotations, RoundEnvironment env)
process()相当于每个处理器的主函数main(), 在这里写你的扫描、评估和处理注解的代码,以及生成Java文件
@Override
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//TypeElement(注解元素所在的类的类元素),BindingSet(注解所在类对应的绑定配置)
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);
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
注:
1、生成TypeElement(注解所在类的类元素)和BindingSet(Activity_ViewBinding生成配置)的map;
2、遍历这个map,拿出typeElement和BindingSet,通过BindingSet生成JavaFile(BindingSet后面再分析);
3、执行javaFile.writeTo(filer)即可生成Activity_ViewBinding。
(3.1) findAndParseTargets()
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
//注解元素所在类元素TypeElement和对应的BindingSet.Builder的map
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
//使用集合Set记录每个注解所在类的类元素,Set保证了enclosingElement不会重复
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
...
// 遍历所有被注解了@BindView的元素
//env.getElementsAnnotatedWith(BindView.class)返回所有被注解了@BindView的元素的集合
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
try {
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
...
/**
* 下面的代码是把子类的BindingSet和其父类的BindingSet关联起来
*/
Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
new ArrayDeque<>(builderMap.entrySet());
//映射类元素TypeElement和其对应BindingSet的map,作为结果返回出去
Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
while (!entries.isEmpty()) {
Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();
TypeElement type = entry.getKey();//key: 类元素
BindingSet.Builder builder = entry.getValue();//value: BindingSet.Builder
//查找erasedTargetNames中是否有type的父类class
TypeElement parentType = findParentType(type, erasedTargetNames);
if (parentType == null) {
//type没有父类,直接记录type和对应的BindingSet
bindingMap.put(type, builder.build());//执行builder.build(),详细查看一下
} else {
BindingSet parentBinding = bindingMap.get(parentType);
if (parentBinding != null) {
builder.setParent(parentBinding);//设置builder的父BindingSet,关联起来
bindingMap.put(type, builder.build());//记录type和对应的BindingSet
} else {
//找到type有父类class,但是父类的BindingSet还没执行build(),把这个map放到后面再操作
entries.addLast(entry);
}
}
}
}
注:
1、遍历所有被注解了@BindView的元素, 执行parseBindView();* (parseBindView()的执行请看3.2)
2、新建一个Map<TypeElement, BindingSet> bindingMap,用于记录typeElement和BindingSet;
3、遍历builderMap和erasedTargetNames,查询每个typeElement在builderMap中是否有其父类,如果typeElement没有父typeElement,执行BindingSet.build()生成BindingSet,把typeElement和生成BindingSet添加入bindingMap;如果typeElement有父typeElement,关联子typeElement和父typeElement的BindingSet,最后把bindingMap返回出去。(BindingSet.build()的执行请看3.5)
(3.2)parseBindView()
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames) {
//获取注解了@BindView控件变量所在的类所对应的TypeElement元素
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
...
// Verify that the target type extends from View.
TypeMirror elementType = element.asType();//获取控件的TypeKind, 如:android.widget.TextView
if (elementType.getKind() == TypeKind.TYPEVAR) {//elementType.getKind():TypeKind.DECLARED
TypeVariable typeVariable = (TypeVariable) elementType;
elementType = typeVariable.getUpperBound();
}
//获取控件view所在类的全限定名,如:com.example.administrator.myannotationprocessor.MainActivity
Name qualifiedName = enclosingElement.getQualifiedName();
//获取注解控件的变量命名,例如image,btn
Name simpleName = element.getSimpleName();
...
// Assemble information on the field.
int id = element.getAnnotation(BindView.class).value();//获取@BindView(ID)的id值
//Builder对象定义了viewBinding的文件名,构造函数,unBind()方法、view所属对象的类型等
BindingSet.Builder builder = builderMap.get(enclosingElement);//获取控件view所在类Element对应的 BindingSet.Builder
Id resourceId = elementToId(element, BindView.class, id);
if (builder != null) {
String existingBindingName = builder.findExistingBindingName(resourceId);
if (existingBindingName != null) {//一个类中不同的控件,id不能相同
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 {
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
}
String name = simpleName.toString();//例:image,btn
TypeName type = TypeName.get(elementType);//例:android.widget.TextView
boolean required = isFieldRequired(element);//判断元素是否包含注解@Nullable(通常情况下不包含,required 为true)
//builder记录resourceId和对应的FieldViewBinding,用于生成viewbinding文件时,创建该view变量
builder.addField(resourceId, new FieldViewBinding(name, type, required));
//记录每个注解所在类的类元素enclosingElement
erasedTargetNames.add(enclosingElement);
}
注:
1、获取注解了@BindView 元素所在类的类元素enclosingElement(TypeElement),
查看builderMap中enclosingElement有没有对应的BindingSet.Builder,如果没有则新建BindingSet.Builder (getOrCreateBindingBuilder()查看3.3),用builderMap记录下这个enclosingElement和这个新建的Builder;
2、获取注解了@BindView 元素的控件id,元素的变量名simpleName和元素类型TypeName,新建一个FieldViewBinding,用Builder记录下id和对应的FieldViewBinding; ( builder.addField()请看3.4)
3、erasedTargetNames记录下enclosingElement;
(3.3)getOrCreateBindingBuilder() (创建BindingSet.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);
//创建好builder后,记录enclosingElement和对应的BindingSet.Builder
builderMap.put(enclosingElement, builder);
}
return builder;
}
-->
static Builder newBuilder(TypeElement enclosingElement) {
TypeMirror typeMirror = enclosingElement.asType();//获取类元素enclosingElement的真实类型
//判断是否为对应类型的子类,这三种类型内部都可能用到控件@BindView绑定
boolean isView = isSubtypeOfType(typeMirror, VIEW_TYPE);
boolean isActivity = isSubtypeOfType(typeMirror, ACTIVITY_TYPE);
boolean isDialog = isSubtypeOfType(typeMirror, DIALOG_TYPE);
//获取enclosingElement的类型名字,Activity/Dialog/View
TypeName targetType = TypeName.get(typeMirror);
if (targetType instanceof ParameterizedTypeName) {
targetType = ((ParameterizedTypeName) targetType).rawType;
}
String packageName = getPackage(enclosingElement).getQualifiedName().toString();//enclosingElement的包名
String className = enclosingElement.getQualifiedName().toString().substring(//enclosingElement的类名,如MainActivity
packageName.length() + 1).replace('.', '$');
//ClassName包括包名和ViewBinding的类名,类名,: 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);
}
注:传入类元素enclosingElement,判断enclosingElement类型, 获取enclosingElement的类型名TypeName ,所在的包名,拼接新的ViewBinding的类名,把这些参数传入Builder(),创建一个新的BindingSet.Builder。
(3.4)builder.addField(resourceId, new FieldViewBinding(name, type, required))
private final Map<Id, ViewBinding.Builder> viewIdMap = new LinkedHashMap<>();
void addField(Id id, FieldViewBinding binding) {
getOrCreateViewBindings(id).setFieldBinding(binding);
}
-->
private ViewBinding.Builder getOrCreateViewBindings(Id id) {
ViewBinding.Builder viewId = viewIdMap.get(id);
if (viewId == null) {
viewId = new ViewBinding.Builder(id);//new一个ViewBinding.Builder
viewIdMap.put(id, viewId);
}
return viewId;
}
-->
public void setFieldBinding(FieldViewBinding fieldBinding) {
...
//设置ViewBinding的fieldBinding
this.fieldBinding = fieldBinding;
}
注:BindingSet.Build中用viewIdMap记录id和对应的FieldViewBinding,viewIdMap在3.4中 会用到;FieldViewBinding记录有控件的变量名,控件的类型名称
(3.4)BindingSet.Build.build() (执行BindingSet的创建)
BindingSet build() {
//ImmutableList是不可变集合,viewBindings.build()执行后,ImmutableList不再可变
ImmutableList.Builder<ViewBinding> viewBindings = ImmutableList.builder();
for (ViewBinding.Builder builder : viewIdMap.values()) {
viewBindings.add(builder.build());
}
return new BindingSet(targetTypeName, bindingClassName, isFinal, isView, isActivity, isDialog,
viewBindings.build(), collectionBindings.build(), resourceBindings.build(),
parentBinding);
}
-->
private BindingSet(TypeName targetTypeName, ClassName bindingClassName, boolean isFinal,
boolean isView, boolean isActivity, boolean isDialog, ImmutableList<ViewBinding> viewBindings,
ImmutableList<FieldCollectionViewBinding> collectionBindings,
ImmutableList<ResourceBinding> resourceBindings, @Nullable BindingSet parentBinding) {
this.isFinal = isFinal;
this.targetTypeName = targetTypeName;//类型名称
this.bindingClassName = bindingClassName;//生成ViewBinding的名称
this.isView = isView;//是否为View
this.isActivity = isActivity;//是否为Activity
this.isDialog = isDialog;//是否为Dialog
this.viewBindings = viewBindings;//记录
this.collectionBindings = collectionBindings;
this.resourceBindings = resourceBindings;
this.parentBinding = parentBinding;//记录父类的ViewBinding,生成的文件要继承它
}
注:这里就只是执行BindingSet的构建,返回一个BindingSet。
小结:以上的代码主要是扫描注解了@BindView的元素列表并遍历这个列表,获取注解元素所在类的类元素和对应的BindingSet的map;再重新查看第3点的process()的方法,可以看到这句代码JavaFile javaFile = binding.brewJava(sdk, debuggable),得知生成JavaFile的关键在BindingSet类中。
后续的分析我们需要配合一个ButterKnifeDemo来进行,编译后生成的MainActivity_ViewBinding 如下;
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
private View view2131165321;
private View view2131165218;
private View view2131165238;
private TextWatcher view2131165238TextWatcher;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
@SuppressLint("ClickableViewAccessibility")
public MainActivity_ViewBinding(final MainActivity target, View source) {
this.target = target;
View view;
view = Utils.findRequiredView(source, R.id.textView, "field 'textView', method 'onViewClicked', method 'onViewClicked', method 'onViewClicked', method 'onViewLongClicked', and method 'onViewTouched'");
target.textView = Utils.castView(view, R.id.textView, "field 'textView'", TextView.class);
view2131165321 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.onViewClicked();
target.onViewClicked(Utils.castParam(p0, "doClick", 0, "onViewClicked", 0, TextView.class));
target.onViewClicked(p0);
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View p0) {
return target.onViewLongClicked();
}
});
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View p0, MotionEvent p1) {
return target.onViewTouched();
}
});
view = Utils.findRequiredView(source, R.id.btn, "method 'onViewClicked'");
view2131165218 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.onViewClicked(p0);
}
});
view = Utils.findRequiredView(source, R.id.editText, "method 'beforeTextChanged', method 'afterTextChanged', and method 'onTextChanged'");
view2131165238 = view;
view2131165238TextWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence p0, int p1, int p2, int p3) {
target.onTextChanged(p0, p1, p2, p3);
}
@Override
public void beforeTextChanged(CharSequence p0, int p1, int p2, int p3) {
target.beforeTextChanged(p0, p1, p2, p3);
}
@Override
public void afterTextChanged(Editable p0) {
target.afterTextChanged(p0);
}
};
((TextView) view).addTextChangedListener(view2131165238TextWatcher);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.textView = null;
view2131165321.setOnClickListener(null);
view2131165321.setOnLongClickListener(null);
view2131165321.setOnTouchListener(null);
view2131165321 = null;
view2131165218.setOnClickListener(null);
view2131165218 = null;
((TextView) view2131165238).removeTextChangedListener(view2131165238TextWatcher);
view2131165238TextWatcher = null;
view2131165238 = null;
}
}
4、brewJava()
final class BindingSet{
JavaFile brewJava(int sdk, boolean debuggable) {
//TypeSpec 用于配置javaFile的变量和方法
TypeSpec bindingConfiguration = createType(sdk, debuggable);
//bindingClassName.packageName()是包名,设置创建javaFile到对应的包中
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);//添加ViewBinding添加属性public
if (isFinal) {
result.addModifiers(FINAL);//final属性
}
if (parentBinding != null) {
result.superclass(parentBinding.bindingClassName);//设置父类ViewBinding
} else {
result.addSuperinterface(UNBINDER);//继承Unbinder接口
}
//创建全局私有变量, 如:private MainActivity target;
if (hasTargetField()) {
result.addField(targetTypeName, "target", PRIVATE);
}
//根据类型,创建对应的构造方法
if (isView) {
result.addMethod(createBindingConstructorForView());
} else if (isActivity) {
//例:public MainActivity_ViewBinding(MainActivity target)
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));
if (hasViewBindings() || parentBinding == null) {
//创建unbind方法, 解析在(4.5)
result.addMethod(createBindingUnbindMethod(result));
}
return result.build();
}
}
注:调用createType()生成TypeSpec,这是生成JavaFile的必要参数; createType()方法可以查看createBindingConstructorForActivity()和createBindingConstructor(),其他基本比较好理解。
(4.1)createBindingConstructorForActivity()
private MethodSpec createBindingConstructorForActivity() {
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
.addAnnotation(UI_THREAD)//给构造方法加上@UiThread的注解
.addModifiers(PUBLIC)//给构造方法加上publc属性
.addParameter(targetTypeName, "target");//加上参数,例如:MainActivity target
if (constructorNeedsView()) {//判断构造方法是否需要传入视图
//传入包括最顶层decorView的视图
builder.addStatement("this(target, target.getWindow().getDecorView())");
} else {
builder.addStatement("this(target, target)");
}
return builder.build();
}
-->
private boolean constructorNeedsView() {
//判断页面或者父类页面是否有设置@BindView或@BindViews注解变量
return hasViewBindings()
|| (parentBinding != null && parentBinding.constructorNeedsView());
}
-->
//当我们的activity或者dialog等页面中有设置注解@BindView的控件变量,viewBindings就不为空
//有设置注解@BindViews,collectionBindings就不为空
private boolean hasViewBindings() {
return !viewBindings.isEmpty() || !collectionBindings.isEmpty();
}
注:判断viewBindings和collectionBindings是否为空,如果有用到@BindView或者@BindViews,hasViewBindings就为true,那么MainActivity_ViewBinding的构造函数就需要view的参数。
(4.2)createBindingConstructorForActivity()
private MethodSpec createBindingConstructor(int sdk, boolean debuggable) {
MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
.addAnnotation(UI_THREAD)
.addModifiers(PUBLIC);
if (hasMethodBindings()) {
//有使用注解@OnClick这些,需要用target回到页面的onclick方法,所以target要加上final
constructor.addParameter(targetTypeName, "target", FINAL);
} else {
constructor.addParameter(targetTypeName, "target");
}
if (constructorNeedsView()) {
//用使用@BindView,需要为页面绑定控件,需要View的参数
constructor.addParameter(VIEW, "source");
} else {
constructor.addParameter(CONTEXT, "context");
}
if (hasUnqualifiedResourceBindings()) {
//如果资源id的id值是一个普通的int值而不是R.id.xxx,给构造方法添加注解@SuppressWarnings
constructor.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "ResourceType")
.build());
}
if (hasOnTouchMethodBindings()) {
//如果有用到@Touch注解,构造方法需要加上@SuppressLint("ClickableViewAccessibility")的注解
constructor.addAnnotation(AnnotationSpec.builder(SUPPRESS_LINT)
.addMember("value", "$S", "ClickableViewAccessibility")
.build());
}
if (parentBinding != null) {//有父类的ViewBinding时
if (parentBinding.constructorNeedsView()) {//父类ViewBinding的构造函数有View类型的参数
constructor.addStatement("super(target, source)");//添加super(target, source)的代码
} else if (constructorNeedsView()) {//父类ViewBinding的构造函数没有View类型的参数,本构造函数需要View类型的参数
constructor.addStatement("super(target, source.getContext())");
} else {//父类和自己的ViewBinding的构造函数都不需要View类型的参数
constructor.addStatement("super(target, context)");
}
constructor.addCode("\n");//
}
if (hasTargetField()) {
//有target变量的时候给它赋值
constructor.addStatement("this.target = target");
constructor.addCode("\n");
}
if (hasViewBindings()) {
if (hasViewLocal()) {
// 加上代码View view,用于暂存控件
constructor.addStatement("$T view", VIEW);
}
for (ViewBinding binding : viewBindings) {//遍历viewBindings集合
addViewBinding(constructor, binding, debuggable);//初始化控件并绑定
}
for (FieldCollectionViewBinding binding : collectionBindings) {
constructor.addStatement("$L", binding.render(debuggable));
}
if (!resourceBindings.isEmpty()) {
constructor.addCode("\n");
}
}
...
return constructor.build();
}
上面这段主要还是配置Activity_ViewBinding构造方法的属性和参数等配置,代码注释也非常清楚,而控件的绑定在addViewBinding()中,继续往下看。
(4.3)addViewBinding()
private void addViewBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
if (binding.isSingleFieldBinding()) {//只绑定控件,不需要添加click或者touch监听
// 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());//添加代码,例如:target.textView
boolean requiresCast = requiresCast(fieldBinding.getType());//如果控件变量的类型不是View,就需要转型
if (!debuggable || (!requiresCast && !fieldBinding.isRequired())) {
if (requiresCast) {
builder.add("($T) ", fieldBinding.getType());//拼接代码:(TextView)
}
//例子:(TextView)source.findViewById(R.id.textView)
builder.add("source.findViewById($L)", binding.getId().code);
} else {
//生成类似Utils.findRequiredViewAsType(source, R.id.textView, "field 'textView'", TextView.class);
builder.add("$T.find", UTILS);//拼接代码:Utils.find
builder.add(fieldBinding.isRequired() ? "RequiredView" : "OptionalView");//拼接代码:RequiredView
if (requiresCast) {
builder.add("AsType");//拼接代码:AsType
}
builder.add("(source, $L", binding.getId().code);//拼接代码:(source, R.id.xxx
if (fieldBinding.isRequired() || requiresCast) {
//拼接代码,例如:,"field ' text' "
builder.add(", $S", asHumanDescription(singletonList(fieldBinding)));
}
if (requiresCast) {
builder.add(", $T.class", fieldBinding.getRawType());//拼接代码,例如:,TextView.class
}
builder.add(")");//拼接括号结尾: )
}
//完成一句代码的构建, 例:target.textView = (TextView)source.findViewById(R.id.textView)
//或者:target.textView = Utils.findRequiredViewAsType(source, R.id.textView, "field 'textView'", TextView.class);
result.addStatement("$L", builder.build());
return;
}
/*
* 当需要给控件添加点击监听时,才执行以下代码
* */
List<MemberViewBinding> requiredBindings = binding.getRequiredBindings();
if (!debuggable || requiredBindings.isEmpty()) {
//view = source.findViewById(R.id.xxx)
result.addStatement("view = source.findViewById($L)", binding.getId().code);
} else if (!binding.isBoundToRoot()) {
//view = Utils.findRequiredView(source, R.id.xxx, "field 'textView' and method 'onViewClicked'")
result.addStatement("view = $T.findRequiredView(source, $L, $S)", UTILS,
binding.getId().code, asHumanDescription(requiredBindings));
}
addFieldBinding(result, binding, debuggable);
addMethodBindings(result, binding, debuggable);
}
注:上面首先判断控件是否只需要绑定id实现初始化,不需要给控件添加监听;然后使用CodeBlock执行代码拼接,拼接结果如:target.textView = Utils.findRequiredViewAsType(source, R.id.textView, "field 'textView'", TextView.class);如果控件不仅仅需要绑定id,还需要实现监听,则需要继续看以下的代码 。
(4.4)addFieldBinding()和addMethodBindings()
//控件需要设置点击监听时,设置控件的绑定
private void addFieldBinding(MethodSpec.Builder result, ViewBinding binding, boolean debuggable) {
FieldViewBinding fieldBinding = binding.getFieldBinding();
if (fieldBinding != null) {
if (requiresCast(fieldBinding.getType())) {//需要转型
if (debuggable) {//debug模式
//target.text = Utils.castView(view, R.id.text, "field 'text'", TextView.class);
result.addStatement("target.$L = $T.castView(view, $L, $S, $T.class)",
fieldBinding.getName(), UTILS, binding.getId().code,
asHumanDescription(singletonList(fieldBinding)), fieldBinding.getRawType());
} else {
//target.text = (TextView)view
result.addStatement("target.$L = ($T) view", fieldBinding.getName(),
fieldBinding.getType());
}
} else {
result.addStatement("target.$L = view", fieldBinding.getName());//不需要转型,target.fieldName= view
}
}
}
//设置控件的绑定
private void addMethodBindings(MethodSpec.Builder result, ViewBinding binding,
boolean debuggable) {
//ListenerClass-->ListenerMethod-->Set<MethodViewBinding>
Map<ListenerClass, Map<ListenerMethod, Set<MethodViewBinding>>> classMethodBindings =
binding.getMethodBindings();
if (classMethodBindings.isEmpty()) {//需要设置监听的方法为空
return;
}
// We only need to emit the null check if there are zero required bindings.
boolean needsNullChecked = binding.getRequiredBindings().isEmpty();
if (needsNullChecked) {
result.beginControlFlow("if (view != null)");
}
// Add the view reference to the binding.
String fieldName = "viewSource";
String bindName = "source";
if (!binding.isBoundToRoot()) {//判断id是否为空
fieldName = "view" + Integer.toHexString(binding.getId().value);//例子:view2131165315
bindName = "view";
}
result.addStatement("$L = $N", fieldName, bindName);//添加代码,例子:view2131165315 = view;
for (Map.Entry<ListenerClass, Map<ListenerMethod, Set<MethodViewBinding>>> e
: classMethodBindings.entrySet()) {
ListenerClass listener = e.getKey();//例如:@OnClick
Map<ListenerMethod, Set<MethodViewBinding>> methodBindings = e.getValue();
//创建一个匿名内部类,父类为listener中的type, 例如:DebouncingOnClickListener
TypeSpec.Builder callback = TypeSpec.anonymousClassBuilder("")
.superclass(ClassName.bestGuess(listener.type()));
//获取ListenerClass的对应的ListenerMethod
for (ListenerMethod method : getListenerMethods(listener)) {
MethodSpec.Builder callbackMethod = MethodSpec.methodBuilder(method.name())
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.returns(bestGuess(method.returnType()));//函数返回类型,default "void"
String[] parameterTypes = method.parameters();
for (int i = 0, count = parameterTypes.length; i < count; i++) {
callbackMethod.addParameter(bestGuess(parameterTypes[i]), "p" + i);//方法的参数, 例:View p0
}
boolean hasReturnValue = false;//默认方法不需要返回值
CodeBlock.Builder builder = CodeBlock.builder();
Set<MethodViewBinding> methodViewBindings = methodBindings.get(method);//获取ListenerMethod对应的MethodViewBinding集合
if (methodViewBindings != null) {
for (MethodViewBinding methodBinding : methodViewBindings) {//遍历MethodViewBinding集合
if (methodBinding.hasReturnValue()) {//如果方法需要返回值,
hasReturnValue = true;
//拼接代码: return
builder.add("return "); // TODO what about multiple methods?
}
builder.add("target.$L(", methodBinding.getName());//拼接代码,例如:target.onViewClicked();
List<Parameter> parameters = methodBinding.getParameters();
String[] listenerParameters = method.parameters();
for (int i = 0, count = parameters.size(); i < count; i++) {
if (i > 0) {
builder.add(", ");//例如,如果target.onViewClicked()中需要多个参数,参数中间加逗号
}
Parameter parameter = parameters.get(i);
int listenerPosition = parameter.getListenerPosition();
if (parameter.requiresCast(listenerParameters[listenerPosition])) {
if (debuggable) {
//拼接代码,例如Utils.castParam(p0, "doClick", 0, "onViewClicked", 0, TextView.class)
builder.add("$T.castParam(p$L, $S, $L, $S, $L, $T.class)", UTILS,
listenerPosition, method.name(), listenerPosition, methodBinding.getName(), i,
parameter.getType());
} else {
builder.add("($T) p$L", parameter.getType(), listenerPosition);
}
} else {
builder.add("p$L", listenerPosition);//拼接代码,例如,p0
}
}
builder.add(");\n");//拼接代码,);
}
}
if (!"void".equals(method.returnType()) && !hasReturnValue) {
builder.add("return $L;\n", method.defaultReturn());//拼接代码:return null;
}
callbackMethod.addCode(builder.build());
callback.addMethod(callbackMethod.build());
}
//只有@OnTextChanged和@OnPageChange两个注解有设置remover
boolean requiresRemoval = listener.remover().length() != 0;
String listenerField = null;
if (requiresRemoval) {
//对于@OnTextChanged, listenerClassName = android.text.TextWatcher
TypeName listenerClassName = bestGuess(listener.type());
listenerField = fieldName + ((ClassName) listenerClassName).simpleName();//例子:view2131165238TextWatcher
result.addStatement("$L = $L", listenerField, callback.build());//view2131165238TextWatcher=new TextWatcher(){...}
}
String targetType = listener.targetType();
if (!VIEW_TYPE.equals(targetType)) {//判断listener的targetType是否为android.view.View;
//对于@OnTextChanged, 拼接完的代码为:((TextView) view).addTextChangedListener(view2131165238TextWatcher);
result.addStatement("(($T) $N).$L($L)", bestGuess(targetType), bindName,
listener.setter(), requiresRemoval ? listenerField : callback.build());
} else {
result.addStatement("$N.$L($L)", bindName, listener.setter(),
requiresRemoval ? listenerField : callback.build());
//最后结果
// view.setOnClickListener(new DebouncingOnClickListener() {
// @Override
// public void doClick(View p0) {
// target.onViewClicked(p0);
// }
// });
}
}
if (needsNullChecked) {
result.endControlFlow();
}
}
注:上面两个方法就是绑定控件以及添加控件监听时如何拼接代码的过程。
(4.5)createBindingUnbindMethod()
private MethodSpec createBindingUnbindMethod(TypeSpec.Builder bindingClass) {
MethodSpec.Builder result = MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addModifiers(PUBLIC);
if (!isFinal && parentBinding == null) {
result.addAnnotation(CALL_SUPER);//添加@CallSuper注解
}
if (hasTargetField()) {
if (hasFieldBindings()) {
result.addStatement("$T target = this.target", targetTypeName);//例如:MainActivity target = this.target
}
//if (target == null) throw new IllegalStateException("Bindings already cleared.");
result.addStatement("if (target == null) throw new $T($S)", IllegalStateException.class,
"Bindings already cleared.");
result.addStatement("$N = null", hasFieldBindings() ? "this.target" : "target");//this.target = null;
result.addCode("\n");
for (ViewBinding binding : viewBindings) {
if (binding.getFieldBinding() != null) {
result.addStatement("target.$L = null", binding.getFieldBinding().getName());//例如:target.text = null;
}
}
for (FieldCollectionViewBinding binding : collectionBindings) {
result.addStatement("target.$L = null", binding.name);//例如:target.text = null;
}
}
if (hasMethodBindings()) {
result.addCode("\n");
for (ViewBinding binding : viewBindings) {
addFieldAndUnbindStatement(bindingClass, result, binding);-->在下一部分
}
}
if (parentBinding != null) {
result.addCode("\n");
result.addStatement("super.unbind()");//super.unbind()
}
return result.build();
}
-->生成代码:给控件设置listener为null, 设置控件为空
private void addFieldAndUnbindStatement(TypeSpec.Builder result, MethodSpec.Builder unbindMethod,
ViewBinding bindings) {
// Only add fields to the binding if there are method bindings.
Map<ListenerClass, Map<ListenerMethod, Set<MethodViewBinding>>> classMethodBindings =
bindings.getMethodBindings();
if (classMethodBindings.isEmpty()) {
return;
}
//当空间的id不为NO_RES_ID, 添加全局变量,例如:private View view2131165315;
String fieldName =
bindings.isBoundToRoot()
? "viewSource"
: "view" + Integer.toHexString(bindings.getId().value);//fieldName:view2131165315;
result.addField(VIEW, fieldName, PRIVATE);
// We only need to emit the null check if there are zero required bindings.
boolean needsNullChecked = bindings.getRequiredBindings().isEmpty();
if (needsNullChecked) {
unbindMethod.beginControlFlow("if ($N != null)", fieldName);
}
for (ListenerClass listenerClass : classMethodBindings.keySet()) {
// We need to keep a reference to the listener
// in case we need to unbind it via a remove method.
boolean requiresRemoval = !"".equals(listenerClass.remover());
String listenerField = "null";
if (requiresRemoval) {
//对于@OnTextChanged, listenerClassName = android.text.TextWatcher
TypeName listenerClassName = bestGuess(listenerClass.type());
listenerField = fieldName + ((ClassName) listenerClassName).simpleName();//view2131165238TextWatcher
result.addField(listenerClassName, listenerField, PRIVATE);//private TextWatcher view2131165238TextWatcher;
}
String targetType = listenerClass.targetType();
if (!VIEW_TYPE.equals(targetType)) {//判断listener的targetType是否为android.view.View;
//例如((TextView) view2131165238).removeTextChangedListener(view2131165238TextWatcher);
unbindMethod.addStatement("(($T) $N).$N($N)", bestGuess(targetType),
fieldName, removerOrSetter(listenerClass, requiresRemoval), listenerField);
} else {
//拼接代码,例如view2131165320.setOnClickListener(null);
unbindMethod.addStatement("$N.$N($N)", fieldName,
removerOrSetter(listenerClass, requiresRemoval), listenerField);
}
if (requiresRemoval) {
unbindMethod.addStatement("$N = null", listenerField);
}
}
unbindMethod.addStatement("$N = null", fieldName);//拼接代码,例如view2131165320 = null;
if (needsNullChecked) {
unbindMethod.endControlFlow();
}
}
注:上面这段代码就是unbind方法生成的过程,给控件设置listener为null获取调用removeListener去掉监听, 设置控件为null,具体可以看代码以及注释。
总结:
1、ButterKnife通过注册自定义注解,注解处理器扫描程序中的所有注解,获取到@BindView注解的元素集合以及元素的信息;
2、对所有@BindView注解元素的信息进行分析,获取@BindView注解元素所在类的类元素enclosingElement,给每个enclosingElement配对一个BindingSet.Build(用map存储),Build记录下enclosingElement类中所有@BindView元素的相关信息(包括id,控件类型,变量名)。
3、执行BindingSet.Build()生成BindingSet(存储有类元素class的实际类型,Activity或View或Dialog,类元素的包名,类变量名_ViewBinding(如MainActivity_ViewBinding),@BindView注解元素的信息等);
4、执行BindingSet.brewJava,根据BindingSet存储的信息,生成对应的JavaFile,最后执行javaFile.writeTo(filer)就能生成Activity_ViewBinding的文件。
网友评论