深入理解ButterKnife源码并掌握原理(二)

好,我们接着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就是word
    String 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.
    //必需是方法类型的,节点元素为ExecutableElement
    if (!(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() {
      }
      //反射注解方法value
    Method 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(