好,我们接着parseBindView的步骤3 ,忘记了在哪里了,咦。

可以看下上一篇,哈哈。
步骤3

   BindingClass bindingClass = targetClassMap.get(enclosingElement);if (bindingClass != null) {ViewBindings viewBindings = bindingClass.getViewBinding(getId(id));if (viewBindings != null && viewBindings.getFieldBinding() != null) {FieldViewBinding existingBinding = viewBindings.getFieldBinding();error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",BindView.class.getSimpleName(), id, existingBinding.getName(),enclosingElement.getQualifiedName(), element.getSimpleName());return;}} else {bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);}

如果map(缓存)中已经有了就直接到到这个BindingClass实例。BindingClass这个我们后面还会再说。

1.如果不为空,则根据id获取保存在bindingClass中的ViewBindings实例,
如果viewBindings不为空且viewBindings.getFieldBinding不为空则抛出异常,什么意思呢?就是说一个id不能bind多次。
2.如果为空,则bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);
获取并返回,参数是最初的那个map和父节点。

  private BindingClass getOrCreateTargetClass(Map<TypeElement, BindingClass> targetClassMap,TypeElement enclosingElement) {BindingClass bindingClass = targetClassMap.get(enclosingElement);//再次判断if (bindingClass == null) {//获取父节点的类型名字,这个不用太关系TypeName targetType = TypeName.get(enclosingElement.asType());if (targetType instanceof ParameterizedTypeName) {targetType = ((ParameterizedTypeName) targetType).rawType;}//获取该enclosingElement就是父节点所在的包名称String packageName = getPackageName(enclosingElement);//类名字String className = getClassName(enclosingElement, packageName);//根据包名称和类名称获取bindingClassName实体//并且加入了_ViewBinding 哈哈,有点意思是了。不是吗??ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");//是否是final 类 boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL);//创建了一个BindingClass实例bindingClass = new BindingClass(targetType, bindingClassName, isFinal);//加入集合,缓存targetClassMap.put(enclosingElement, bindingClass);}return bindingClass;}

到此我们的parseBindView的步骤3就完了。
步骤4:parseBindView步骤4

//@BindView(R.id.word)// TextView word;  //name就是wordString name = element.getSimpleName().toString();//类型的名字TypeName type = TypeName.get(elementType);//是否要求可为空boolean required = isFieldRequired(element);//生成FieldViewBinding实体FieldViewBinding binding = new FieldViewBinding(name, type, required);

步骤5.

添加到bindingClass中的成员变量的实体的集合中,方便生成java源文件也就是xxxxx_ViewBinding文件的成员变量的初始化存在
bindingClass.addField(getId(id), binding);

其它的注解都是一样的。至此查找并解析成员变量的流程就完了。
接下来是处理控件事件的监听的流程。

注解事件源码流程分析(OnClick,OnItemClick等)

我们回到findAndParseTargets方法。

   //... 省略成员变量的注解// Process each annotation that corresponds to a listener.//处理方法的比如一些OnClick,OnItemClick等for (Class<? extends Annotation> listener : LISTENERS) {findAndParseListener(env, listener, targetClassMap, erasedTargetNames);}// Try to find a parent binder for each.for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {TypeElement parentType = findParentType(entry.getKey(), erasedTargetNames);if (parentType != null) {BindingClass bindingClass = entry.getValue();BindingClass parentBindingClass = targetClassMap.get(parentType);bindingClass.setParent(parentBindingClass);}}return targetClassMap;}

处理注解事件同样也分为查找和解析2个大步骤。
LISTENERS是butterknife支持的注解集合

  private static final List<Class<? extends Annotation>> 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 //);
 private void findAndParseListener(RoundEnvironment env,Class<? extends Annotation> annotationClass, Map<TypeElement, BindingClass> targetClassMap,Set<TypeElement> erasedTargetNames) {for (Element element : env.getElementsAnnotatedWith(annotationClass)) {//检查合法性问题if (!SuperficialValidation.validateElement(element)) continue;try {//解析注解parseListenerAnnotation(annotationClass, element, targetClassMap, erasedTargetNames);} catch (Exception e) {StringWriter stackTrace = new StringWriter();e.printStackTrace(new PrintWriter(stackTrace));error(element, "Unable to generate view binder for @%s.\n\n%s",annotationClass.getSimpleName(), stackTrace.toString());}}}

我们看一下parseListenerAnnotation方法,传入了注解类annotationClass,该节点element,最初的那个集合targetClassMap。
比较长,我在方法里注释效果会比较好,哈哈

private void parseListenerAnnotation(Class<? extends Annotation> annotationClass, Element element,Map<TypeElement, BindingClass> targetClassMap, Set<TypeElement> erasedTargetNames)throws Exception {// This should be guarded by the annotation's @Target but it's worth a check for safe casting.//必需是方法类型的,节点元素为ExecutableElementif (!(element instanceof ExecutableElement) || element.getKind() != METHOD) {throw new IllegalStateException(String.format("@%s annotation must be on a method.", annotationClass.getSimpleName()));}//方法对应的是ExecutableElement,前文我们已经简单的说明了一下ExecutableElement executableElement = (ExecutableElement) element;//获取父节点TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();// Assemble information on the method.// 获取注解信息Annotation annotation = element.getAnnotation(annotationClass);//该注解value方法,每一个注解都有(butterknife提供的都是数组)//为什么是数组?因为支持下面这种@OnClick({R.id.hello,R.id.hello2}) void sayHello() {}//反射注解方法valueMethod annotationValue = annotationClass.getDeclaredMethod("value");//不是数组抛出异常if (annotationValue.getReturnType() != int[].class) {throw new IllegalStateException(String.format("@%s annotation value() type not int[].", annotationClass));}//反射调用int[] ids = (int[]) annotationValue.invoke(annotation);//方法名字String name = executableElement.getSimpleName().toString();boolean required = isListenerRequired(executableElement);// Verify that the method and its containing class are accessible via generated code.//检查方法的修饰符,和成员变量一样,这里就不写了,嘻嘻boolean hasError = isInaccessibleViaGeneratedCode(annotationClass, "methods", element);hasError |= isBindingInWrongPackage(annotationClass, element);//一个注解的方法不能有形同的id,or抛出异常Integer duplicateId = findDuplicate(ids);if (duplicateId != null) {error(element, "@%s annotation for method contains duplicate ID %d. (%s.%s)",annotationClass.getSimpleName(), duplicateId, enclosingElement.getQualifiedName(),element.getSimpleName());hasError = true;}//获取该注解ListenerClass.class注解,什么意思呢?就是   //butterknife提供的方法注解 包含了另外一个注解//可以跳过代码看下面的文字说明。ListenerClass listener = annotationClass.getAnnotation(ListenerClass.class);if (listener == null) {throw new IllegalStateException(String.format("No @%s defined on @%s.", ListenerClass.class.getSimpleName(),annotationClass.getSimpleName()));}//检查id的合法性,里面有个Optional注解for (int id : ids) {//id 为 -1 ,不合法     if (id == NO_ID.value) {if (ids.length == 1) {//一个参数情况,且方法的参数适用了Optional注解,则抛出异常if (!required) {error(element, "ID-free binding must not be annotated with @Optional. (%s.%s)",enclosingElement.getQualifiedName(), element.getSimpleName());hasError = true;}} else {error(element, "@%s annotation contains invalid ID %d. (%s.%s)",annotationClass.getSimpleName(), id, enclosingElement.getQualifiedName(),element.getSimpleName());hasError = true;}}}//获取实现的方法ListenerMethod method;ListenerMethod[] methods = listener.method();// methods就是OnItemClick 注解的ListenerClass注解的初始化值,比如下面这种,肯定是个//    method = @ListenerMethod(//   name = "onItemClick",//   parameters = {//   "android.widget.AdapterView<?>",//    "android.view.View",//        "int",//        "long"//    }//  )if (methods.length > 1) {//抛异常,不可能走到这因为butterknife提供的都是1个默认的,能骗到我,我可是上过小学的人,哈哈throw new IllegalStateException(String.format("Multiple listener methods specified on @%s.",annotationClass.getSimpleName()));} else if (methods.length == 1) {//如果有method属性值即这种onItemClick,则callbacks必须为空,也就是2者不能同时使用if (listener.callbacks() != ListenerClass.NONE.class) {throw new IllegalStateException(String.format("Both method() and callback() defined on @%s.",annotationClass.getSimpleName()));}method = methods[0];} else {// 否则使用callback//反射ListenerClass注解中的callback方法Method annotationCallback = annotationClass.getDeclaredMethod("callback");Enum<?> callback = (Enum<?>) annotationCallback.invoke(annotation);Field callbackField = callback.getDeclaringClass().getField(callback.name());method = callbackField.getAnnotation(ListenerMethod.class);//如果没有ListenerMethod.class注解 抛出异常,也就是说你使用了callback,则必须提供ListenerMethod.class注解if (method == null) {throw new IllegalStateException(String.format("No @%s defined on @%s's %s.%s.", ListenerMethod.class.getSimpleName(),annotationClass.getSimpleName(), callback.getDeclaringClass().getSimpleName(),callback.name()));}}//检查方法的合法性,就是你使用的注解的方法的参数不能butterknife的参数的个数(也就是android系统的那种)// Verify that the method has equal to or less than the number of parameters as the listener.List<? extends VariableElement> methodParameters = executableElement.getParameters();if (methodParameters.size() > method.parameters().length) {error(element, "@%s methods can have at most %s parameter(s). (%s.%s)",annotationClass.getSimpleName(), method.parameters().length,enclosingElement.getQualifiedName(), element.getSimpleName());hasError = true;}//检查返回值,就是你使用的注解的方法的参数不能butterknife的参数的个数(也就是android系统的那种)// Verify method return type matches the listener.TypeMirror returnType = executableElement.getReturnType();if (returnType instanceof TypeVariable) {TypeVariable typeVariable = (TypeVariable) returnType;returnType = typeVariable.getUpperBound();}if (!returnType.toString().equals(method.returnType())) {error(element, "@%s methods must have a '%s' return type. (%s.%s)",annotationClass.getSimpleName(), method.returnType(),enclosingElement.getQualifiedName(), element.getSimpleName());hasError = true;}if (hasError) {return;}//下面是方法参数的检查,不做分析了,太细了。记住一点就行了,你写的不和系统的实现方法一样就抛出异常Parameter[] parameters = Parameter.NONE;if (!methodParameters.isEmpty()) {parameters = new Parameter[methodParameters.size()];BitSet methodParameterUsed = new BitSet(methodParameters.size());String[] parameterTypes = method.parameters();for (int i = 0; i < methodParameters.size(); i++) {VariableElement methodParameter = methodParameters.get(i);TypeMirror methodParameterType = methodParameter.asType();if (methodParameterType instanceof TypeVariable) {TypeVariable typeVariable = (TypeVariable) methodParameterType;methodParameterType = typeVariable.getUpperBound();}for (int j = 0; j < parameterTypes.length; j++) {if (methodParameterUsed.get(j)) {continue;}if (isSubtypeOfType(methodParameterType, parameterTypes[j])|| isInterface(methodParameterType)) {parameters[i] = new Parameter(j, TypeName.get(methodParameterType));methodParameterUsed.set(j);break;}}if (parameters[i] == null) {StringBuilder builder = new StringBuilder();builder.append("Unable to match @").append(annotationClass.getSimpleName()).append(" method arguments. (").append(enclosingElement.getQualifiedName()).append('.').append(element.getSimpleName()).append(')');for (int j = 0; j < parameters.length; j++) {Parameter parameter = parameters[j];builder.append("\n\n  Parameter #").append(j + 1).append(": ").append(methodParameters.get(j).asType().toString()).append("\n    ");if (parameter == null) {builder.append("did not match any listener parameters");} else {builder.append("matched listener parameter #").append(parameter.getListenerPosition() + 1).append(": ").append(parameter.getType());}}builder.append("\n\nMethods may have up to ").append(method.parameters().length).append(" parameter(s):\n");for (String parameterType : method.parameters()) {builder.append("\n  ").append(parameterType);}builder.append("\n\nThese may be listed in any order but will be searched for from top to bottom.");error(executableElement, builder.toString());return;}}}//最后构造MethodViewBinding实体,形成方法的实体MethodViewBinding binding = new MethodViewBinding(name, Arrays.asList(parameters), required);//构造BindingClassBindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);for (int id : ids) {//将生成的方法加入到bindingClass的方法集合中,一切都是为了生存java代码而准备。if (!bindingClass.addMethod(getId(id), listener, method, binding)) {error(element, "Multiple listener methods with return value specified for ID %d. (%s.%s)",id, enclosingElement.getQualifiedName(), element.getSimpleName());return;}}// Add the type-erased version to the valid binding targets set.erasedTargetNames.add(enclosingElement);}

ListenerClass/ListenerMethod 注解说明

@Target(METHOD)
@Retention(CLASS)
@ListenerClass(targetType = "android.widget.AdapterView<?>",setter = "setOnItemClickListener",type = "android.widget.AdapterView.OnItemClickListener",method = @ListenerMethod(name = "onItemClick",parameters = {"android.widget.AdapterView<?>","android.view.View","int","long"})
)
public @interface OnItemClick {/** View IDs to which the method will be bound. */@IdRes int[] value() default { View.NO_ID };
}
import java.lang.annotation.Retention;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@Retention(RUNTIME) @Target(ANNOTATION_TYPE)
public @interface ListenerClass {String targetType();/** Name of the setter method on the {@linkplain #targetType() target type} for the listener. */String setter();/*** Name of the method on the {@linkplain #targetType() target type} to remove the listener. If* empty {@link #setter()} will be used by default.*/String remover() default "";/** Fully-qualified class name of the listener type. */String type();/** Enum which declares the listener callback methods. Mutually exclusive to {@link #method()}. */Class<? extends Enum<?>> callbacks() default NONE.class;/*** Method data for single-method listener callbacks. Mutually exclusive with {@link #callbacks()}* and an error to specify more than one value.*/ListenerMethod[] method() default { };/** Default value for {@link #callbacks()}. */enum NONE { }
}
package butterknife.internal;import java.lang.annotation.Retention;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@Retention(RUNTIME) @Target(FIELD)
public @interface ListenerMethod {/** Name of the listener method for which this annotation applies. */String name();/** List of method parameters. If the type is not a primitive it must be fully-qualified. */String[] parameters() default { };/** Primitive or fully-qualified return type of the listener method. May also be {@code void}. */String returnType() default "void";/** If {@link #returnType()} is not {@code void} this value is returned when no binding exists. */String defaultReturn() default "null";
}

可以把这3个整体来看。ListenerMethod 这个注解包含了方法的返回值,名字,参数,是实现的那些方法;ListenerClass是set的那些方法属性,包含setter等,我们看到了OnItemClick设置的值就是我们平常写的那种,嘻嘻。

至此,我们的findAndParseTargets方法算是走完了。里面有很多细节。
为什么要分析有关细节呢?可以学习下大神的方法和理解有关处理的细节啊,哈哈。
深入理解ButterKnife源码并掌握原理(三)

深入理解ButterKnife源码并掌握原理(二)相关推荐

  1. 深入理解ButterKnife源码并掌握原理(四)

    到此我们整个的流程算分析完了. 最后我们看下对外提供的API bind 方法 那么还差一步,什么时候都要我们生成的java文件呢?答案是: ButterKnife.bind(this);方法. 我们看 ...

  2. 深入理解ButterKnife源码并掌握原理(三)

    上两篇我们分析完了处理器的process方法的findAndParseTargets方法来获取了一个集合,该集合包含了你使用注解的类的TypeElement和这个类中的注解的实例BindingClas ...

  3. 深入理解ButterKnife源码并掌握原理(一)

    前言 话说在android这座大山里,有一座庙(方块公司-square),庙里住着一个神-jake(我是这么叫的嘻嘻). 不要小看这个小jake,这个神可是为android应用开发们提供了强有力的帮助 ...

  4. 深入理解ButterKnife源码并掌握原理(五)

    前面四篇是自己参考有关知识和分析ButterKnife源码后写的,如果有不对的地方,还望指导下. 这一篇是实践篇.我们的目的是学会原理来变为自己的知识. 这个demo所使用的原理就是ButterKni ...

  5. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  6. Tomcat7.0源码分析——请求原理分析(上)

    前言 谈起Tomcat的诞生,最早可以追溯到1995年.近20年来,Tomcat始终是使用最广泛的Web服务器,由于其使用Java语言开发,所以广为Java程序员所熟悉.很多早期的J2EE项目,由程序 ...

  7. 【赠书福利】掘金爆火小册同名《Spring Boot源码解读与原理剖析》正式出书了!...

    关注我们丨文末赠书 承载着作者的厚望,掘金爆火小册同名读物<Spring Boot源码解读与原理剖析>正式出书! 本书前身是掘金社区销量TOP的小册--<Spring Boot源码解 ...

  8. 遍历HashMap源码——红黑树原理、HashMap红黑树实现与反树型化(三)

    本章将是HashMap源码的最后一章,将介绍红黑树及其实现,HashMap的remove方法与反树型化.长文预警~~ 遍历HashMap源码--红黑树原理.HashMap红黑树实现与反树型化 什么是红 ...

  9. Java基础笔记(2)——HashMap的源码,实现原理,底层结构是怎么样的

    Java基础笔记(2)--HashMap的源码,实现原理,底层结构是怎么样的 HashMap的源码,实现原理,底层结构 1.HashMap: HashMap是基于哈希表的 Map 接口的实现.此实现提 ...

最新文章

  1. 【BETA】Mac技巧之查看苹果电脑 Mac OS X 系统是否开启 64 位运算,以及设置 32/64 位模式的方法...
  2. oracelp---随意 记录(nvl)
  3. malloc的内存分配原理
  4. 版本不一致_一致哈希:Beyond the basics
  5. 手把手教你接入前端热门抓包神器 - whistle
  6. ssh(Spring+Spring mvc+hibernate)——Emp.hbm.xml
  7. NCrawler爬取中文网页时乱码问题的解决方法
  8. 房费制 它 结账BUG
  9. 深入解析C++ STL中的常用容器
  10. Java并发包-原子类
  11. HTML5前端教程分享:JavaScript学习指南
  12. openwrt 自动签到插件-食用指南
  13. 上百所大学计算机考研有变化!22计算机考研太难了!
  14. 【js练习】鼠标移入和移出事件
  15. python神经网络编程 豆瓣,神经网络算法python实现
  16. AndroidStudio 编译报错 abc_list_selector_disabled_holo_light.9.png
  17. 人脸服务器如何与门禁系统对接,人脸识别门禁系统功能介绍
  18. 一个由Dubbo Thread pool is EXHAUSTED引发的问题排查
  19. Centos安装google浏览器01
  20. 解决打不开 xxx.github.io的万能解决方法

热门文章

  1. [NLP]OpenNLP Maven工程的依赖
  2. 数据库版本管理工具Flyway应用
  3. mongo源码学习(四)服务入口点ServiceEntryPoint
  4. 01-执行上下文与变量对象
  5. Oracle 数据块损坏与恢复具体解释
  6. 从零开始部署基于阿里容器云的微服务(consul+registrator+template)(一)
  7. EL表达式处理字符串 是否 包含 某字符串 截取 拆分...............
  8. Discuz! X2.5 添加风格模板
  9. WebUI Case(1): www.swt-designer.com 首页
  10. mft文件记录属性头包括_学懂主流NTFS分区文件系统,你也可以成为MM眼中的大神!...