目标
- 实现原理
- 设计模式
分析
基于上面已知信息 接下来分析源码:
ButterKnifeProcessor#init
// TODO: 18-6-4 获取辅助类
@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));
// TODO: 18-6-25 typeUtils 用来处理TypeMirror的工具类
typeUtils = env.getTypeUtils();
// TODO: 18-6-25 filer 用来生成java的源文件
filer = env.getFiler();
try {
// TODO: 18-6-25 语法分析树
trees = Trees.instance(processingEnv);
} catch (IllegalArgumentException ignored) {
}
}
支持的注解:
@Override public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
for (Class extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
private Set> getSupportedAnnotations() {
Set> 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;
}
private static final List> LISTENERS = Arrays.asList(//
OnCheckedChanged.class,
OnClick.class,
OnEditorAction.class,
OnFocusChange.class,
OnItemClick.class,
OnItemLongClick.class,
OnItemSelected.class,
OnLongClick.class,
OnPageChange.class,
OnTextChanged.class,
OnTouch.class
);
ButterKnifeProcessor#process:核心方法
// 关键方法
@Override
public boolean process(Set extends TypeElement> elements, RoundEnvironment env) {
// 查找所有的注解 并且根据注解生成BindingSet
Map bindingMap = findAndParseTargets(env);
// 遍历步骤map 生成.java文件也就是类名_ViewBinding的java文件
for (Map.Entry entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
// 使用开源库javapoet生成Java文件
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;
}
// TODO: 18-6-19 RoundEnvironment 表示当前的运行环境 用于查找对应的注解
private Map findAndParseTargets(RoundEnvironment env) {
// TODO: 18-6-4 之所以使用 LinkedHashMap 在于其具有arraylist的有序以及hashmap的查询快的优势
Map builderMap = new LinkedHashMap<>();
Set erasedTargetNames = new LinkedHashSet<>();
// TODO: 18-6-19 处理注解
// Process each @BindAnim element.
for (Element element : env.getElementsAnnotatedWith(BindAnim.class)) {
if (!SuperficialValidation.validateElement(element)) continue;
try {
parseResourceAnimation(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindAnim.class, e);
}
}
......... //省略部分代码
// TODO: 18-6-19 对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);
}
}
......... //省略部分代码
// TODO: 18-6-19 处理事件
// Process each annotation that corresponds to a listener.
for (Class extends Annotation> listener : LISTENERS) {
findAndParseListener(env, listener, builderMap, erasedTargetNames);
}
// 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).
// TODO: 18-6-19 将Map.Entry转化为Map
Deque> entries =
new ArrayDeque<>(builderMap.entrySet());
Map bindingMap = new LinkedHashMap<>();
while (!entries.isEmpty()) {
Map.Entry 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;
}
ButterKnifeProcessor#parseBindView
// TODO: 18-6-19 Element代表源代码 ;TypeElement代表源代码中的元素类型
private void parseBindView(Element element, Map builderMap,
Set erasedTargetNames) {
// TODO: 18-6-19 getEnclosingElement 获取父节点
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// TODO: 18-6-19 检查用户的合法性
// Start by verifying common generated code restrictions.
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
|| isBindingInWrongPackage(BindView.class, element);
// TODO: 18-6-19 TypeMirror用于获取类的信息
// 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;
}
// TODO: 18-6-19 获取id
// Assemble information on the field.
int id = element.getAnnotation(BindView.class).value();
// TODO: 18-6-19 从 builderMap中取出Builder 此处的每一个builder对应项目中的某一个类 比如绑定的是MainActivity 那么就是对应MainActivity
// TODO: 18-6-19 BindingSet.Builder用来构建BindingSet和ViewBinding的中间产物
BindingSet.Builder builder = builderMap.get(enclosingElement);
Id resourceId = elementToId(element, BindView.class, id);
if (builder != null) {
// TODO: 18-6-19 检查是否已经存在此资源id
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 {
// TODO: 18-6-19 创建builder 并且存进builderMap中
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);
}
ButterKnifeProcessor#isInaccessibleViaGeneratedCode
// TODO: 18-6-19 检查用户的合法性其中一种检验标准
private boolean isInaccessibleViaGeneratedCode(Class extends Annotation> annotationClass,
String targetThing, Element element) {
boolean hasError = false;
// TODO: 18-6-19 获取父节点
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// TODO: 18-6-19 检查修饰符 如果包含PRIVATE或者STATIC就抛出异常
// Verify field or method modifiers.
Set modifiers = element.getModifiers();
if (modifiers.contains(PRIVATE) || modifiers.contains(STATIC)) {
error(element, "@%s %s must not be private or static. (%s.%s)",
annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
element.getSimpleName());
hasError = true;
}
// TODO: 18-6-19 检查父节点是否是类
// Verify containing type.
if (enclosingElement.getKind() != CLASS) {
error(enclosingElement, "@%s %s may only be contained in classes. (%s.%s)",
annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
element.getSimpleName());
hasError = true;
}
// TODO: 18-6-19 检查父类是否是私有类
// Verify containing class visibility is not private.
if (enclosingElement.getModifiers().contains(PRIVATE)) {
error(enclosingElement, "@%s %s may not be contained in private classes. (%s.%s)",
annotationClass.getSimpleName(), targetThing, enclosingElement.getQualifiedName(),
element.getSimpleName());
hasError = true;
}
return hasError;
}
ButterKnifeProcessor#isBindingInWrongPackage
// TODO: 18-6-19 不能在sdk以及jdk的源码中使用
private boolean isBindingInWrongPackage(Class extends Annotation> annotationClass,
Element element) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
String qualifiedName = enclosingElement.getQualifiedName().toString();
// TODO: 18-6-19 筛选出sdk
if (qualifiedName.startsWith("android.")) {
error(element, "@%s-annotated class incorrectly in Android framework package. (%s)",
annotationClass.getSimpleName(), qualifiedName);
return true;
}
// TODO: 18-6-19 筛选出jdk
if (qualifiedName.startsWith("java.")) {
error(element, "@%s-annotated class incorrectly in Java framework package. (%s)",
annotationClass.getSimpleName(), qualifiedName);
return true;
}
return false;
}
ButterKnifeProcessor#Builder
// TODO: 18-6-19 此类对应我们要生成代码所需要的所有信息 看名字就知道使用了建造者模式
static final class Builder {
// TODO: 18-6-19 有注解的对应类的类名
private final TypeName targetTypeName;
// TODO: 18-6-19 注解器生成的类的类名
private final ClassName bindingClassName;
// TODO: 18-6-19 标记是不是final view activity 以及dialog
private final boolean isFinal;
private final boolean isView;
private final boolean isActivity;
private final boolean isDialog;
private BindingSet parentBinding;
// TODO: 18-6-19 一个id对应一个 ViewBinding.Builder
private final Map viewIdMap = new LinkedHashMap<>();
private final ImmutableList.Builder collectionBindings =
ImmutableList.builder();
private final ImmutableList.Builder resourceBindings = ImmutableList.builder();
BindingSet#getOrCreateViewBindings
private ViewBinding.Builder getOrCreateViewBindings(Id id) {
ViewBinding.Builder viewId = viewIdMap.get(id);
if (viewId == null) {
// TODO: 18-6-25 构建Builder
viewId = new ViewBinding.Builder(id);
viewIdMap.put(id, viewId);
}
return viewId;
}
到这里BindView
分析完成
接下来分析事件的绑定